Generate random passwords instead of hard-coding
* The prototype stage hard-coding of passwords is replaced by random generation of passwords for: * all API services; * RabbitMQ; * MySQL; * OpenStack admin user; * OpenStack service users; * Passwords are not replaced upon successive microstack.init calls to preserve idempotency. Change-Id: Ic3d6108a81d09bdd09e986f80b3040b030605178
This commit is contained in:
parent
71ad68d36a
commit
32ad5af7f4
@ -1,107 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Initialize Config
|
|
||||||
# TODO: put this in a nice yaml format, and parse it.
|
|
||||||
|
|
||||||
# General snap-wide settings
|
|
||||||
snapctl set \
|
|
||||||
config.clustered=false \
|
|
||||||
config.post-setup=true \
|
|
||||||
;
|
|
||||||
|
|
||||||
snapctl set \
|
|
||||||
config.keystone.region-name=microstack \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Networking related settings.
|
|
||||||
snapctl set \
|
|
||||||
config.network.dns-servers=1.1.1.1 \
|
|
||||||
config.network.dns-domain=microstack.example. \
|
|
||||||
config.network.ext-gateway=10.20.20.1 \
|
|
||||||
config.network.control-ip=10.20.20.1 \
|
|
||||||
config.network.node-fqdn=`hostname -f` \
|
|
||||||
config.network.compute-ip=10.20.20.1 \
|
|
||||||
config.network.ext-cidr=10.20.20.1/24 \
|
|
||||||
config.network.security-rules=true \
|
|
||||||
config.network.dashboard-allowed-hosts="*" \
|
|
||||||
config.network.ports.dashboard=80 \
|
|
||||||
config.network.ports.mysql=3306 \
|
|
||||||
config.network.ports.rabbit=5672 \
|
|
||||||
config.network.external-bridge-name=br-ex \
|
|
||||||
config.network.physnet-name=physnet1 \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Passwords, certs, etc.
|
|
||||||
snapctl set \
|
|
||||||
config.credentials.os-password=keystone \
|
|
||||||
config.credentials.key-pair="/home/{USER}/snap/{SNAP_NAME}/common/.ssh/id_microstack" \
|
|
||||||
config.credentials.nova-password=nova \
|
|
||||||
config.credentials.cinder-password=cinder \
|
|
||||||
config.credentials.neutron-password=neutron \
|
|
||||||
config.credentials.placement-password=placement \
|
|
||||||
config.credentials.glance-password=glance \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Cinder volume backend config.
|
|
||||||
snapctl set \
|
|
||||||
config.cinder.setup-loop-based-cinder-lvm-backend=false \
|
|
||||||
config.cinder.loop-device-file-size=32G \
|
|
||||||
config.cinder.lvm-backend-volume-group=cinder-volumes \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Host optimizations and fixes.
|
|
||||||
snapctl set \
|
|
||||||
config.host.ip-forwarding=false \
|
|
||||||
config.host.check-qemu=true \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Enable or disable groups of services.
|
|
||||||
snapctl set \
|
|
||||||
config.services.control-plane=true \
|
|
||||||
config.services.hypervisor=true \
|
|
||||||
config.services.spice-console=true \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Clustering roles
|
|
||||||
snapctl set \
|
|
||||||
config.cluster.role=control \
|
|
||||||
config.cluster.password=null \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Uninstall stuff
|
|
||||||
snapctl set \
|
|
||||||
config.cleanup.delete-bridge=true \
|
|
||||||
config.cleanup.remove=true \
|
|
||||||
;
|
|
||||||
|
|
||||||
# Filebeat
|
|
||||||
snapctl set \
|
|
||||||
config.logging.datatag="" \
|
|
||||||
config.logging.host="localhost:5044" \
|
|
||||||
config.logging.custom-config="$SNAP_COMMON/etc/filebeat/filebeat-microstack.yaml" \
|
|
||||||
config.services.extra.enabled=false \
|
|
||||||
config.services.extra.filebeat=false \
|
|
||||||
;
|
|
||||||
|
|
||||||
# services won't start if disabled in install hook
|
|
||||||
# see https://github.com/snapcore/snapd/blob/53dd10a71d1754610eda2d3d776465b81b3281cd/wrappers/services.go#L139
|
|
||||||
snapctl stop --disable ${SNAP_NAME}.filebeat
|
|
||||||
|
|
||||||
# NRPE
|
|
||||||
snapctl set \
|
|
||||||
config.alerting.custom-config="$SNAP_COMMON/etc/nrpe/nrpe-microstack.cfg" \
|
|
||||||
config.services.extra.nrpe=false \
|
|
||||||
;
|
|
||||||
|
|
||||||
snapctl stop --disable ${SNAP_NAME}.telegraf
|
|
||||||
|
|
||||||
# Telegraf
|
|
||||||
snapctl set \
|
|
||||||
config.monitoring.ipmi="" \
|
|
||||||
config.monitoring.custom-config="$SNAP_COMMON/etc/telegraf/telegraf-microstack.conf" \
|
|
||||||
config.services.extra.telegraf=false \
|
|
||||||
;
|
|
||||||
|
|
||||||
snapctl stop --disable ${SNAP_NAME}.nrpe
|
|
88
snap-overlay/bin/set-default-config.py
Executable file
88
snap-overlay/bin/set-default-config.py
Executable file
@ -0,0 +1,88 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
|
||||||
|
from init import shell
|
||||||
|
from init import credentials
|
||||||
|
|
||||||
|
|
||||||
|
def _get_default_config():
|
||||||
|
snap_common = os.getenv('SNAP_COMMON')
|
||||||
|
return {
|
||||||
|
'config.clustered': False,
|
||||||
|
'config.post-setup': True,
|
||||||
|
'config.keystone.region-name': 'microstack',
|
||||||
|
'config.credentials.key-pair': '/home/{USER}/snap/{SNAP_NAME}'
|
||||||
|
'/common/.ssh/id_microstack',
|
||||||
|
'config.network.node-fqdn': socket.getfqdn(),
|
||||||
|
'config.network.dns-servers': '1.1.1.1',
|
||||||
|
'config.network.dns-domain': 'microstack.example.',
|
||||||
|
'config.network.ext-gateway': '10.20.20.1',
|
||||||
|
'config.network.control-ip': '10.20.20.1',
|
||||||
|
'config.network.compute-ip': '10.20.20.1',
|
||||||
|
'config.network.ext-cidr': '10.20.20.1/24',
|
||||||
|
'config.network.security-rules': True,
|
||||||
|
'config.network.dashboard-allowed-hosts': '*',
|
||||||
|
'config.network.ports.dashboard': 80,
|
||||||
|
'config.network.ports.mysql': 3306,
|
||||||
|
'config.network.ports.rabbit': 5672,
|
||||||
|
'config.network.external-bridge-name': 'br-ex',
|
||||||
|
'config.network.physnet-name': 'physnet1',
|
||||||
|
'config.cinder.setup-loop-based-cinder-lvm-backend': False,
|
||||||
|
'config.cinder.loop-device-file-size': '32G',
|
||||||
|
'config.cinder.lvm-backend-volume-group': 'cinder-volumes',
|
||||||
|
'config.host.ip-forwarding': False,
|
||||||
|
'config.host.check-qemu': True,
|
||||||
|
'config.services.control-plane': True,
|
||||||
|
'config.services.hypervisor': True,
|
||||||
|
'config.services.spice-console': True,
|
||||||
|
'config.cluster.role': 'control',
|
||||||
|
'config.cluster.password': 'null',
|
||||||
|
'config.cleanup.delete-bridge': True,
|
||||||
|
'config.cleanup.remove': True,
|
||||||
|
'config.logging.custom-config': f'{snap_common}/etc/filebeat'
|
||||||
|
'/filebeat-microstack.yaml',
|
||||||
|
'config.logging.datatag': '',
|
||||||
|
'config.logging.host': 'localhost:5044',
|
||||||
|
'config.services.extra.enabled': False,
|
||||||
|
'config.services.extra.filebeat': False,
|
||||||
|
'config.alerting.custom-config': f'{snap_common}/etc/nrpe'
|
||||||
|
'/nrpe-microstack.cfg',
|
||||||
|
'config.services.extra.nrpe': False,
|
||||||
|
'config.monitoring.ipmi': '',
|
||||||
|
'config.services.extra.telegraf': False,
|
||||||
|
'config.monitoring.custom-config': f'{snap_common}/etc/telegraf'
|
||||||
|
'/telegraf-microstack.conf'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _set_default_config():
|
||||||
|
shell.config_set(**_get_default_config())
|
||||||
|
|
||||||
|
|
||||||
|
def _setup_secrets():
|
||||||
|
# If a user runs init multiple times we do not want to generate
|
||||||
|
# new credentials to keep the init operation idempotent.
|
||||||
|
existing_creds = shell.config_get('config.credentials')
|
||||||
|
if isinstance(existing_creds, dict):
|
||||||
|
existing_cred_keys = existing_creds.keys()
|
||||||
|
else:
|
||||||
|
existing_cred_keys = []
|
||||||
|
shell.config_set(**{
|
||||||
|
k: credentials.generate_password() for k in [
|
||||||
|
'config.credentials.mysql-root-password',
|
||||||
|
'config.credentials.rabbitmq-password',
|
||||||
|
'config.credentials.keystone-password',
|
||||||
|
'config.credentials.nova-password',
|
||||||
|
'config.credentials.cinder-password',
|
||||||
|
'config.credentials.neutron-password',
|
||||||
|
'config.credentials.placement-password',
|
||||||
|
'config.credentials.glance-password',
|
||||||
|
] if k not in existing_cred_keys
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
_set_default_config()
|
||||||
|
_setup_secrets()
|
@ -4,5 +4,7 @@ set -ex
|
|||||||
|
|
||||||
export HOME=$SNAP_COMMON/lib/rabbitmq
|
export HOME=$SNAP_COMMON/lib/rabbitmq
|
||||||
|
|
||||||
$SNAP/usr/sbin/rabbitmqctl add_user openstack rabbitmq || true
|
rabbitmq_password=`snapctl get config.credentials.rabbitmq-password`
|
||||||
|
|
||||||
|
$SNAP/usr/sbin/rabbitmqctl add_user openstack $rabbitmq_password || true
|
||||||
$SNAP/usr/sbin/rabbitmqctl set_permissions openstack ".*" ".*" ".*"
|
$SNAP/usr/sbin/rabbitmqctl set_permissions openstack ".*" ".*" ".*"
|
||||||
|
@ -83,13 +83,13 @@ setup:
|
|||||||
"{snap_common}/etc/microstack.json": 0644
|
"{snap_common}/etc/microstack.json": 0644
|
||||||
snap-config-keys:
|
snap-config-keys:
|
||||||
region_name: 'config.keystone.region-name'
|
region_name: 'config.keystone.region-name'
|
||||||
ospassword: 'config.credentials.os-password'
|
keystone_password: 'config.credentials.keystone-password'
|
||||||
nova_password: 'config.credentials.nova-password'
|
nova_password: 'config.credentials.nova-password'
|
||||||
cinder_password: 'config.credentials.cinder-password'
|
cinder_password: 'config.credentials.cinder-password'
|
||||||
neutron_password: 'config.credentials.neutron-password'
|
neutron_password: 'config.credentials.neutron-password'
|
||||||
placement_password: 'config.credentials.placement-password'
|
|
||||||
glance_password: 'config.credentials.glance-password'
|
glance_password: 'config.credentials.glance-password'
|
||||||
placement_password: 'config.credentials.placement-password'
|
placement_password: 'config.credentials.placement-password'
|
||||||
|
rabbitmq_password: 'config.credentials.rabbitmq-password'
|
||||||
control_ip: 'config.network.control-ip'
|
control_ip: 'config.network.control-ip'
|
||||||
node_fqdn: 'config.network.node-fqdn'
|
node_fqdn: 'config.network.node-fqdn'
|
||||||
compute_ip: 'config.network.compute-ip'
|
compute_ip: 'config.network.compute-ip'
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[database]
|
[database]
|
||||||
connection = mysql+pymysql://cinder:cinder@{{ control_ip }}:{{ mysql_port }}/cinder
|
connection = mysql+pymysql://cinder:{{ cinder_password }}@{{ control_ip }}:{{ mysql_port }}/cinder
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
transport_url = rabbit://openstack:rabbitmq@{{ control_ip }}:{{ rabbit_port }}
|
transport_url = rabbit://openstack:{{ rabbitmq_password }}@{{ control_ip }}:{{ rabbit_port }}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[database]
|
[database]
|
||||||
connection = mysql+pymysql://glance:glance@{{ control_ip }}:{{ mysql_port }}/glance
|
connection = mysql+pymysql://glance:{{ glance_password }}@{{ control_ip }}:{{ mysql_port }}/glance
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[database]
|
[database]
|
||||||
connection = mysql+pymysql://keystone:keystone@{{ control_ip }}:{{ mysql_port }}/keystone
|
connection = mysql+pymysql://keystone:{{ keystone_password }}@{{ control_ip }}:{{ mysql_port }}/keystone
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"openstack": {
|
"openstack": {
|
||||||
"admin": {
|
"admin": {
|
||||||
"password": "{{ ospassword }}",
|
"password": "{{ keystone_password }}",
|
||||||
"project_domain_name": "default",
|
"project_domain_name": "default",
|
||||||
"project_name": "admin",
|
"project_name": "admin",
|
||||||
"user_domain_name": "default",
|
"user_domain_name": "default",
|
||||||
|
@ -2,7 +2,7 @@ export OS_PROJECT_DOMAIN_NAME=default
|
|||||||
export OS_USER_DOMAIN_NAME=default
|
export OS_USER_DOMAIN_NAME=default
|
||||||
export OS_PROJECT_NAME=admin
|
export OS_PROJECT_NAME=admin
|
||||||
export OS_USERNAME=admin
|
export OS_USERNAME=admin
|
||||||
export OS_PASSWORD={{ ospassword }}
|
export OS_PASSWORD={{ keystone_password }}
|
||||||
export OS_AUTH_URL=http://{{ control_ip }}:5000
|
export OS_AUTH_URL=http://{{ control_ip }}:5000
|
||||||
export OS_IDENTITY_API_VERSION=3
|
export OS_IDENTITY_API_VERSION=3
|
||||||
export OS_IMAGE_API_VERSION=2
|
export OS_IMAGE_API_VERSION=2
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
transport_url = rabbit://openstack:rabbitmq@{{ control_ip }}:{{ rabbit_port }}
|
transport_url = rabbit://openstack:{{ rabbitmq_password }}@{{ control_ip }}:{{ rabbit_port }}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[database]
|
[database]
|
||||||
connection = mysql+pymysql://neutron:neutron@{{ control_ip }}:{{ mysql_port }}/neutron
|
connection = mysql+pymysql://neutron:{{ neutron_password }}@{{ control_ip }}:{{ mysql_port }}/neutron
|
||||||
|
@ -7,6 +7,7 @@ use_journal = True
|
|||||||
# Set a hostname to be an FQDN to avoid issues with port binding for
|
# Set a hostname to be an FQDN to avoid issues with port binding for
|
||||||
# which a hostname of a Nova node must match a hostname of an OVN chassis.
|
# which a hostname of a Nova node must match a hostname of an OVN chassis.
|
||||||
host = {{ node_fqdn }}
|
host = {{ node_fqdn }}
|
||||||
|
my_ip = {{ compute_ip }}
|
||||||
|
|
||||||
[oslo_concurrency]
|
[oslo_concurrency]
|
||||||
# Oslo Concurrency lock path
|
# Oslo Concurrency lock path
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[database]
|
[database]
|
||||||
connection = mysql+pymysql://nova:nova@{{ control_ip }}:{{ mysql_port }}/nova
|
connection = mysql+pymysql://nova:{{ nova_password }}@{{ control_ip }}:{{ mysql_port }}/nova
|
||||||
|
|
||||||
[api_database]
|
[api_database]
|
||||||
connection = mysql+pymysql://nova_api:nova_api@{{ control_ip }}:{{ mysql_port }}/nova_api
|
connection = mysql+pymysql://nova:{{ nova_password }}@{{ control_ip }}:{{ mysql_port }}/nova_api
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
[glance]
|
[glance]
|
||||||
api_servers = http://{{ control_ip }}:9292
|
service_type = image
|
||||||
|
service_name = glance
|
||||||
|
region_name = {{ region_name }}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
transport_url = rabbit://openstack:rabbitmq@{{ control_ip }}:{{ rabbit_port }}
|
transport_url = rabbit://openstack:{{ rabbitmq_password }}@{{ control_ip }}:{{ rabbit_port }}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
[placement_database]
|
[placement_database]
|
||||||
connection = mysql+pymysql://placement:placement@{{ control_ip }}:{{ mysql_port }}/placement
|
connection = mysql+pymysql://placement:{{ placement_password }}@{{ control_ip }}:{{ mysql_port }}/placement
|
||||||
|
@ -46,10 +46,25 @@ port=${PORT}
|
|||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
|
init_sql() {
|
||||||
|
# Write out the initial SQL statements to execute on mysql initialization.
|
||||||
|
mkdir -p "${CONFDIR}"
|
||||||
|
touch ${INIT_SQL_FILE}
|
||||||
|
chmod 600 ${INIT_SQL_FILE}
|
||||||
|
echo "Setting a root user password..."
|
||||||
|
root_password=`snapctl get config.credentials.mysql-root-password`
|
||||||
|
cat > ${INIT_SQL_FILE} <<EOF
|
||||||
|
ALTER USER 'root'@'localhost' IDENTIFIED BY '$root_password';
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
init_database() {
|
init_database() {
|
||||||
echo "Initializing new database in ${DATADIR}..."
|
echo "Initializing new database in ${DATADIR}..."
|
||||||
mkdir -p ${DATADIR}
|
mkdir -p ${DATADIR}
|
||||||
mysqld --defaults-file=${CONFFILE} --initialize-insecure --user=root
|
init_sql
|
||||||
|
mysqld --defaults-file=${CONFFILE} --init-file=${INIT_SQL_FILE} --initialize-insecure --user=root
|
||||||
|
# Get rid of the file with the root password after initialization.
|
||||||
|
rm -f ${INIT_SQL_FILE}
|
||||||
echo "Done"
|
echo "Done"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,6 +80,8 @@ RUNDIR="${MYSQL_SNAPDIR}/run/mysql"
|
|||||||
LOGDIR="${MYSQL_SNAPDIR}/log/mysql"
|
LOGDIR="${MYSQL_SNAPDIR}/log/mysql"
|
||||||
CONFDIR="${MYSQL_SNAPDIR}/etc/mysql"
|
CONFDIR="${MYSQL_SNAPDIR}/etc/mysql"
|
||||||
CONFFILE="${CONFDIR}/my.cnf"
|
CONFFILE="${CONFDIR}/my.cnf"
|
||||||
|
# A snap-specific temporary directory is used.
|
||||||
|
INIT_SQL_FILE="/tmp/init.sql"
|
||||||
FILESDIR="${MYSQL_SNAPDIR}/lib/mysql-files"
|
FILESDIR="${MYSQL_SNAPDIR}/lib/mysql-files"
|
||||||
BASEDIR="${SNAP}/usr"
|
BASEDIR="${SNAP}/usr"
|
||||||
PORT=$(snapctl get config.network.ports.mysql)
|
PORT=$(snapctl get config.network.ports.mysql)
|
||||||
|
@ -3,7 +3,7 @@ set -ex
|
|||||||
|
|
||||||
|
|
||||||
# Initialize config
|
# Initialize config
|
||||||
set-default-config
|
set-default-config.py
|
||||||
|
|
||||||
# TODO(dmitriis): disable other services and only enable them once the
|
# TODO(dmitriis): disable other services and only enable them once the
|
||||||
# prerequisites are met instead of allowing snapd to start them and make them fail.
|
# prerequisites are met instead of allowing snapd to start them and make them fail.
|
||||||
@ -14,6 +14,23 @@ set-default-config
|
|||||||
# 3. Interfaces that do not have auto-connection enabled are manually connected by
|
# 3. Interfaces that do not have auto-connection enabled are manually connected by
|
||||||
# an operator (connecting openvswitch-support loads the openvswitch kernel module
|
# an operator (connecting openvswitch-support loads the openvswitch kernel module
|
||||||
# but auto-connection is not enabled for openvswitch-support).
|
# but auto-connection is not enabled for openvswitch-support).
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nginx
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.keystone-uwsgi
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.placement-uwsgi
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nova-api
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nova-compute
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nova-conductor
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nova-api-metadata
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nova-spicehtml5proxy
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nova-scheduler
|
||||||
|
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.neutron-api
|
||||||
|
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.glance-api
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.registry
|
||||||
|
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.cinder-uwsgi
|
||||||
|
|
||||||
snapctl stop --disable $SNAP_INSTANCE_NAME.ovsdb-server
|
snapctl stop --disable $SNAP_INSTANCE_NAME.ovsdb-server
|
||||||
snapctl stop --disable $SNAP_INSTANCE_NAME.neutron-ovn-metadata-agent
|
snapctl stop --disable $SNAP_INSTANCE_NAME.neutron-ovn-metadata-agent
|
||||||
snapctl stop --disable $SNAP_INSTANCE_NAME.ovn-ovsdb-server-sb
|
snapctl stop --disable $SNAP_INSTANCE_NAME.ovn-ovsdb-server-sb
|
||||||
@ -32,6 +49,9 @@ snapctl stop --disable $SNAP_INSTANCE_NAME.setup-lvm-loopdev
|
|||||||
# Will only be enabled if a backend is chosen to be configured by the user.
|
# Will only be enabled if a backend is chosen to be configured by the user.
|
||||||
snapctl stop --disable $SNAP_INSTANCE_NAME.cinder-volume
|
snapctl stop --disable $SNAP_INSTANCE_NAME.cinder-volume
|
||||||
|
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.filebeat
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.nrpe
|
||||||
|
snapctl stop --disable $SNAP_INSTANCE_NAME.telegraf
|
||||||
|
|
||||||
mkdir -p $SNAP_DATA/lib/libvirt/images
|
mkdir -p $SNAP_DATA/lib/libvirt/images
|
||||||
mkdir -p ${SNAP_COMMON}/log/libvirt/qemu
|
mkdir -p ${SNAP_COMMON}/log/libvirt/qemu
|
||||||
|
@ -11,7 +11,7 @@ if [ -z "$(snapctl get config)" ]; then
|
|||||||
# config values under a config tree. Set the default values now (the
|
# config values under a config tree. Set the default values now (the
|
||||||
# old values were not documented, and we assume that they were not
|
# old values were not documented, and we assume that they were not
|
||||||
# set).
|
# set).
|
||||||
set-default-config
|
set-default-config.py
|
||||||
|
|
||||||
# Make a place for our horizon config overrides to live. We piggy
|
# Make a place for our horizon config overrides to live. We piggy
|
||||||
# back on the above check, because the changes were made
|
# back on the above check, because the changes were made
|
||||||
|
@ -252,6 +252,10 @@ class Framework(unittest.TestCase):
|
|||||||
dashboard_port = check_output(*host.prefix, 'sudo', 'snap', 'get',
|
dashboard_port = check_output(*host.prefix, 'sudo', 'snap', 'get',
|
||||||
'microstack',
|
'microstack',
|
||||||
'config.network.ports.dashboard')
|
'config.network.ports.dashboard')
|
||||||
|
keystone_password = check_output(
|
||||||
|
*host.prefix, 'sudo', 'snap', 'get',
|
||||||
|
'microstack',
|
||||||
|
'config.credentials.keystone-password')
|
||||||
self.driver.get("http://{}:{}/".format(
|
self.driver.get("http://{}:{}/".format(
|
||||||
host.horizon_ip,
|
host.horizon_ip,
|
||||||
dashboard_port
|
dashboard_port
|
||||||
@ -259,7 +263,8 @@ class Framework(unittest.TestCase):
|
|||||||
# Login to horizon!
|
# Login to horizon!
|
||||||
self.driver.find_element(By.ID, "id_username").click()
|
self.driver.find_element(By.ID, "id_username").click()
|
||||||
self.driver.find_element(By.ID, "id_username").send_keys("admin")
|
self.driver.find_element(By.ID, "id_username").send_keys("admin")
|
||||||
self.driver.find_element(By.ID, "id_password").send_keys("keystone")
|
self.driver.find_element(By.ID, "id_password").send_keys(
|
||||||
|
keystone_password)
|
||||||
self.driver.find_element(By.CSS_SELECTOR, "#loginBtn > span").click()
|
self.driver.find_element(By.CSS_SELECTOR, "#loginBtn > span").click()
|
||||||
# Verify that we can click something on the dashboard -- e.g.,
|
# Verify that we can click something on the dashboard -- e.g.,
|
||||||
# we're still not sitting at the login screen.
|
# we're still not sitting at the login screen.
|
||||||
|
@ -4,7 +4,8 @@ import json
|
|||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
from cluster.shell import check, check_output
|
from cluster import shell
|
||||||
|
from cluster.shell import check_output
|
||||||
|
|
||||||
|
|
||||||
def join():
|
def join():
|
||||||
@ -27,12 +28,10 @@ def join():
|
|||||||
resp.json))
|
resp.json))
|
||||||
resp = resp.json()
|
resp = resp.json()
|
||||||
|
|
||||||
# TODO: add better error handling to the below
|
credentials = resp['config']['credentials']
|
||||||
os_password = resp['config']['credentials']['os-password']
|
control_creds = {f'config.credentials.{k}': v
|
||||||
|
for k, v in credentials.items()}
|
||||||
# Set passwords and such
|
shell.config_set(**control_creds)
|
||||||
check('snapctl', 'set', 'config.credentials.os-password={}'.format(
|
|
||||||
os_password))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
import pymysql
|
import pymysql
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import json
|
||||||
|
|
||||||
|
|
||||||
def sql(cmd) -> None:
|
def sql(cmd) -> None:
|
||||||
@ -16,7 +17,9 @@ def sql(cmd) -> None:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
mysql_conf = '${SNAP_USER_COMMON}/etc/mysql/my.cnf'.format(**os.environ)
|
mysql_conf = '${SNAP_USER_COMMON}/etc/mysql/my.cnf'.format(**os.environ)
|
||||||
|
root_pasword = config_get('config.credentials.mysql-root-password')
|
||||||
connection = pymysql.connect(host='localhost', user='root',
|
connection = pymysql.connect(host='localhost', user='root',
|
||||||
|
password=root_pasword,
|
||||||
read_default_file=mysql_conf)
|
read_default_file=mysql_conf)
|
||||||
|
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
@ -36,3 +39,19 @@ def check(*args):
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
return subprocess.check_call(args, env=os.environ)
|
return subprocess.check_call(args, env=os.environ)
|
||||||
|
|
||||||
|
|
||||||
|
def config_get(*keys):
|
||||||
|
"""Get snap config keys via snapctl.
|
||||||
|
|
||||||
|
:param keys list[str]: Keys to retrieve from the snap configuration.
|
||||||
|
"""
|
||||||
|
return json.loads(check_output('snapctl', 'get', '-t', *keys))
|
||||||
|
|
||||||
|
|
||||||
|
def config_set(**kwargs):
|
||||||
|
"""Get snap config keys via snapctl.
|
||||||
|
|
||||||
|
:param kwargs dict[str, str]: Values to set in the snap configuration.
|
||||||
|
"""
|
||||||
|
check_output('snapctl', 'set', *[f'{k}={v}' for k, v in kwargs.items()])
|
||||||
|
9
tools/init/init/credentials.py
Normal file
9
tools/init/init/credentials.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import string
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
DEFAULT_PASSWORD_LENGTH = 32
|
||||||
|
|
||||||
|
|
||||||
|
def generate_password(length=DEFAULT_PASSWORD_LENGTH):
|
||||||
|
return ''.join(secrets.choice(
|
||||||
|
string.ascii_letters + string.digits) for i in range(length))
|
@ -150,8 +150,8 @@ def init() -> None:
|
|||||||
questions.RabbitMq(),
|
questions.RabbitMq(),
|
||||||
questions.DatabaseSetup(),
|
questions.DatabaseSetup(),
|
||||||
questions.PlacementSetup(),
|
questions.PlacementSetup(),
|
||||||
questions.NovaHypervisor(),
|
|
||||||
questions.NovaControlPlane(),
|
questions.NovaControlPlane(),
|
||||||
|
questions.NovaHypervisor(),
|
||||||
questions.NovaSpiceConsoleSetup(),
|
questions.NovaSpiceConsoleSetup(),
|
||||||
questions.NeutronControlPlane(),
|
questions.NeutronControlPlane(),
|
||||||
questions.GlanceSetup(),
|
questions.GlanceSetup(),
|
||||||
|
@ -7,7 +7,7 @@ for now, we're keeping things simple (if a big lengthy)
|
|||||||
|
|
||||||
----------------------------------------------------------------------
|
----------------------------------------------------------------------
|
||||||
|
|
||||||
Copyright 2019 Canonical Ltd
|
Copyright 2020 Canonical Ltd
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
@ -27,6 +27,7 @@ import json
|
|||||||
from time import sleep
|
from time import sleep
|
||||||
from os import path
|
from os import path
|
||||||
|
|
||||||
|
from init import shell
|
||||||
from init.shell import (check, call, check_output, sql, nc_wait, log_wait,
|
from init.shell import (check, call, check_output, sql, nc_wait, log_wait,
|
||||||
start, restart, download, disable, enable)
|
start, restart, download, disable, enable)
|
||||||
from init.config import Env, log
|
from init.config import Env, log
|
||||||
@ -247,12 +248,10 @@ class NetworkSettings(Question):
|
|||||||
class OsPassword(ConfigQuestion):
|
class OsPassword(ConfigQuestion):
|
||||||
_type = 'string'
|
_type = 'string'
|
||||||
_question = 'Openstack Admin Password'
|
_question = 'Openstack Admin Password'
|
||||||
config_key = 'config.credentials.os-password'
|
config_key = 'config.credentials.keystone-password'
|
||||||
|
|
||||||
def yes(self, answer):
|
def yes(self, answer):
|
||||||
_env['ospassword'] = answer
|
_env['keystone_password'] = answer
|
||||||
|
|
||||||
# TODO obfuscate the password!
|
|
||||||
|
|
||||||
|
|
||||||
class ForceQemu(Question):
|
class ForceQemu(Question):
|
||||||
@ -367,19 +366,24 @@ class DatabaseSetup(Question):
|
|||||||
'mysqld: ready for connections.')
|
'mysqld: ready for connections.')
|
||||||
|
|
||||||
def _create_dbs(self) -> None:
|
def _create_dbs(self) -> None:
|
||||||
# TODO: actually use passwords here.
|
db_creds = shell.config_get('config.credentials')
|
||||||
for db in ('neutron', 'nova', 'nova_api', 'nova_cell0', 'cinder',
|
for service_user, db_name in (
|
||||||
'glance', 'keystone', 'placement'):
|
('neutron', 'neutron'),
|
||||||
sql("CREATE USER IF NOT EXISTS '{db}'@'{control_ip}'"
|
('nova', 'nova'),
|
||||||
" IDENTIFIED BY '{db}';".format(db=db, **_env))
|
('nova', 'nova_api'),
|
||||||
sql("CREATE DATABASE IF NOT EXISTS `{db}`;".format(db=db))
|
('nova', 'nova_cell0'),
|
||||||
sql("GRANT ALL PRIVILEGES ON {db}.* TO '{db}'@'{control_ip}';"
|
('cinder', 'cinder'),
|
||||||
"".format(db=db, **_env))
|
('glance', 'glance'),
|
||||||
|
('keystone', 'keystone'),
|
||||||
# Grant nova user access to cell0
|
('placement', 'placement')
|
||||||
sql(
|
):
|
||||||
"GRANT ALL PRIVILEGES ON nova_cell0.* TO 'nova'@'{control_ip}';"
|
db_password = db_creds[f'{service_user}-password']
|
||||||
"".format(**_env))
|
sql("CREATE USER IF NOT EXISTS '{user}'@'{control_ip}'"
|
||||||
|
" IDENTIFIED BY '{db_password}';".format(
|
||||||
|
user=service_user, db_password=db_password, **_env))
|
||||||
|
sql("CREATE DATABASE IF NOT EXISTS `{db}`;".format(db=db_name))
|
||||||
|
sql("GRANT ALL PRIVILEGES ON {db}.* TO '{user}'@'{control_ip}';"
|
||||||
|
"".format(db=db_name, user=service_user, **_env))
|
||||||
|
|
||||||
def _bootstrap(self) -> None:
|
def _bootstrap(self) -> None:
|
||||||
|
|
||||||
@ -389,7 +393,7 @@ class DatabaseSetup(Question):
|
|||||||
bootstrap_url = 'http://{control_ip}:5000/v3/'.format(**_env)
|
bootstrap_url = 'http://{control_ip}:5000/v3/'.format(**_env)
|
||||||
|
|
||||||
check('snap-openstack', 'launch', 'keystone-manage', 'bootstrap',
|
check('snap-openstack', 'launch', 'keystone-manage', 'bootstrap',
|
||||||
'--bootstrap-password', _env['ospassword'],
|
'--bootstrap-password', _env['keystone_password'],
|
||||||
'--bootstrap-admin-url', bootstrap_url,
|
'--bootstrap-admin-url', bootstrap_url,
|
||||||
'--bootstrap-internal-url', bootstrap_url,
|
'--bootstrap-internal-url', bootstrap_url,
|
||||||
'--bootstrap-public-url', bootstrap_url,
|
'--bootstrap-public-url', bootstrap_url,
|
||||||
@ -449,17 +453,6 @@ class NovaHypervisor(Question):
|
|||||||
|
|
||||||
def yes(self, answer):
|
def yes(self, answer):
|
||||||
log.info('Configuring nova compute hypervisor ...')
|
log.info('Configuring nova compute hypervisor ...')
|
||||||
|
|
||||||
if not call('openstack', 'service', 'show', 'compute'):
|
|
||||||
check('openstack', 'service', 'create', '--name', 'nova',
|
|
||||||
'--description', '"Openstack Compute"', 'compute')
|
|
||||||
# TODO make sure that we are the control plane before executing
|
|
||||||
# TODO if control plane is not hypervisor, still create this
|
|
||||||
for endpoint in ['public', 'internal', 'admin']:
|
|
||||||
call('openstack', 'endpoint', 'create', '--region',
|
|
||||||
'microstack', 'compute', endpoint,
|
|
||||||
'http://{compute_ip}:8774/v2.1'.format(**_env))
|
|
||||||
|
|
||||||
start('nova-compute')
|
start('nova-compute')
|
||||||
|
|
||||||
def no(self, answer):
|
def no(self, answer):
|
||||||
@ -492,8 +485,12 @@ class PlacementSetup(Question):
|
|||||||
log.info('Configuring the Placement service...')
|
log.info('Configuring the Placement service...')
|
||||||
|
|
||||||
if not call('openstack', 'user', 'show', 'placement'):
|
if not call('openstack', 'user', 'show', 'placement'):
|
||||||
check('openstack', 'user', 'create', '--domain', 'default',
|
check(
|
||||||
'--password', 'placement', 'placement')
|
'openstack', 'user', 'create', '--domain', 'default',
|
||||||
|
'--password',
|
||||||
|
shell.config_get('config.credentials.placement-password'),
|
||||||
|
'placement',
|
||||||
|
)
|
||||||
check('openstack', 'role', 'add', '--project', 'service',
|
check('openstack', 'role', 'add', '--project', 'service',
|
||||||
'--user', 'placement', 'admin')
|
'--user', 'placement', 'admin')
|
||||||
|
|
||||||
@ -549,8 +546,12 @@ class NovaControlPlane(Question):
|
|||||||
log.info('Configuring nova control plane services ...')
|
log.info('Configuring nova control plane services ...')
|
||||||
|
|
||||||
if not call('openstack', 'user', 'show', 'nova'):
|
if not call('openstack', 'user', 'show', 'nova'):
|
||||||
check('openstack', 'user', 'create', '--domain',
|
check(
|
||||||
'default', '--password', 'nova', 'nova')
|
'openstack', 'user', 'create', '--domain', 'default',
|
||||||
|
'--password',
|
||||||
|
shell.config_get('config.credentials.nova-password'),
|
||||||
|
'nova'
|
||||||
|
)
|
||||||
check('openstack', 'role', 'add', '--project',
|
check('openstack', 'role', 'add', '--project',
|
||||||
'service', '--user', 'nova', 'admin')
|
'service', '--user', 'nova', 'admin')
|
||||||
|
|
||||||
@ -561,7 +562,7 @@ class NovaControlPlane(Question):
|
|||||||
start('nova-api')
|
start('nova-api')
|
||||||
|
|
||||||
log.info('Running Nova API DB migrations'
|
log.info('Running Nova API DB migrations'
|
||||||
' (this will take a lot of time)...')
|
' (this may take a lot of time)...')
|
||||||
check('snap-openstack', 'launch', 'nova-manage', 'api_db', 'sync')
|
check('snap-openstack', 'launch', 'nova-manage', 'api_db', 'sync')
|
||||||
|
|
||||||
if 'cell0' not in check_output('snap-openstack', 'launch',
|
if 'cell0' not in check_output('snap-openstack', 'launch',
|
||||||
@ -577,7 +578,7 @@ class NovaControlPlane(Question):
|
|||||||
'create_cell', '--name=cell1', '--verbose')
|
'create_cell', '--name=cell1', '--verbose')
|
||||||
|
|
||||||
log.info('Running Nova DB migrations'
|
log.info('Running Nova DB migrations'
|
||||||
' (this will take a lot of time)...')
|
' (this may take a lot of time)...')
|
||||||
check('snap-openstack', 'launch', 'nova-manage', 'db', 'sync')
|
check('snap-openstack', 'launch', 'nova-manage', 'db', 'sync')
|
||||||
|
|
||||||
restart('nova-api')
|
restart('nova-api')
|
||||||
@ -594,7 +595,16 @@ class NovaControlPlane(Question):
|
|||||||
|
|
||||||
sleep(5) # TODO: log_wait
|
sleep(5) # TODO: log_wait
|
||||||
|
|
||||||
|
if not call('openstack', 'service', 'show', 'compute'):
|
||||||
|
check('openstack', 'service', 'create', '--name', 'nova',
|
||||||
|
'--description', '"Openstack Compute"', 'compute')
|
||||||
|
for endpoint in ['public', 'internal', 'admin']:
|
||||||
|
call('openstack', 'endpoint', 'create', '--region',
|
||||||
|
'microstack', 'compute', endpoint,
|
||||||
|
'http://{control_ip}:8774/v2.1'.format(**_env))
|
||||||
|
|
||||||
log.info('Creating default flavors...')
|
log.info('Creating default flavors...')
|
||||||
|
|
||||||
self._flavors()
|
self._flavors()
|
||||||
|
|
||||||
def no(self, answer):
|
def no(self, answer):
|
||||||
@ -618,8 +628,12 @@ class CinderSetup(Question):
|
|||||||
log.info('Configuring the Cinder services...')
|
log.info('Configuring the Cinder services...')
|
||||||
|
|
||||||
if not call('openstack', 'user', 'show', 'cinder'):
|
if not call('openstack', 'user', 'show', 'cinder'):
|
||||||
check('openstack', 'user', 'create', '--domain', 'default',
|
check(
|
||||||
'--password', 'cinder', 'cinder')
|
'openstack', 'user', 'create', '--domain', 'default',
|
||||||
|
'--password',
|
||||||
|
shell.config_get('config.credentials.cinder-password'),
|
||||||
|
'cinder'
|
||||||
|
)
|
||||||
check('openstack', 'role', 'add', '--project', 'service',
|
check('openstack', 'role', 'add', '--project', 'service',
|
||||||
'--user', 'cinder', 'admin')
|
'--user', 'cinder', 'admin')
|
||||||
|
|
||||||
@ -699,8 +713,11 @@ class NeutronControlPlane(Question):
|
|||||||
log.info('Configuring Neutron')
|
log.info('Configuring Neutron')
|
||||||
|
|
||||||
if not call('openstack', 'user', 'show', 'neutron'):
|
if not call('openstack', 'user', 'show', 'neutron'):
|
||||||
check('openstack', 'user', 'create', '--domain', 'default',
|
check(
|
||||||
'--password', 'neutron', 'neutron')
|
'openstack', 'user', 'create', '--domain', 'default',
|
||||||
|
'--password',
|
||||||
|
shell.config_get('config.credentials.neutron-password'),
|
||||||
|
'neutron')
|
||||||
check('openstack', 'role', 'add', '--project', 'service',
|
check('openstack', 'role', 'add', '--project', 'service',
|
||||||
'--user', 'neutron', 'admin')
|
'--user', 'neutron', 'admin')
|
||||||
|
|
||||||
@ -810,8 +827,12 @@ class GlanceSetup(Question):
|
|||||||
log.info('Configuring Glance ...')
|
log.info('Configuring Glance ...')
|
||||||
|
|
||||||
if not call('openstack', 'user', 'show', 'glance'):
|
if not call('openstack', 'user', 'show', 'glance'):
|
||||||
check('openstack', 'user', 'create', '--domain', 'default',
|
check(
|
||||||
'--password', 'glance', 'glance')
|
'openstack', 'user', 'create', '--domain', 'default',
|
||||||
|
'--password',
|
||||||
|
shell.config_get('config.credentials.glance-password'),
|
||||||
|
'glance'
|
||||||
|
)
|
||||||
check('openstack', 'role', 'add', '--project', 'service',
|
check('openstack', 'role', 'add', '--project', 'service',
|
||||||
'--user', 'glance', 'admin')
|
'--user', 'glance', 'admin')
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import netifaces
|
|||||||
import pymysql
|
import pymysql
|
||||||
import socket
|
import socket
|
||||||
import wget
|
import wget
|
||||||
|
import json
|
||||||
|
|
||||||
from init.config import Env, log
|
from init.config import Env, log
|
||||||
|
|
||||||
@ -105,7 +106,9 @@ def sql(cmd: str) -> None:
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
mysql_conf = '{SNAP_COMMON}/etc/mysql/my.cnf'.format(**_env)
|
mysql_conf = '{SNAP_COMMON}/etc/mysql/my.cnf'.format(**_env)
|
||||||
connection = pymysql.connect(read_default_file=mysql_conf)
|
root_pasword = config_get('config.credentials.mysql-root-password')
|
||||||
|
connection = pymysql.connect(read_default_file=mysql_conf,
|
||||||
|
password=root_pasword)
|
||||||
|
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.execute(cmd)
|
cursor.execute(cmd)
|
||||||
@ -169,6 +172,22 @@ def disable(service: str) -> None:
|
|||||||
check('snapctl', 'stop', '--disable', 'microstack.{}'.format(service))
|
check('snapctl', 'stop', '--disable', 'microstack.{}'.format(service))
|
||||||
|
|
||||||
|
|
||||||
|
def config_get(*keys):
|
||||||
|
"""Get snap config keys via snapctl.
|
||||||
|
|
||||||
|
:param keys list[str]: Keys to retrieve from the snap configuration.
|
||||||
|
"""
|
||||||
|
return json.loads(check_output('snapctl', 'get', '-t', *keys))
|
||||||
|
|
||||||
|
|
||||||
|
def config_set(**kwargs):
|
||||||
|
"""Get snap config keys via snapctl.
|
||||||
|
|
||||||
|
:param kwargs dict[str, str]: Values to set in the snap configuration.
|
||||||
|
"""
|
||||||
|
check_output('snapctl', 'set', *[f'{k}={v}' for k, v in kwargs.items()])
|
||||||
|
|
||||||
|
|
||||||
def download(url: str, output: str) -> None:
|
def download(url: str, output: str) -> None:
|
||||||
"""Download a file to a path"""
|
"""Download a file to a path"""
|
||||||
wget.download(url, output)
|
wget.download(url, output)
|
||||||
|
Loading…
Reference in New Issue
Block a user