Move UndercloudPostDeployment to python
Configuring Nova (quota, flavors) and Mistral (workbooks, workflows, etc.) is a lot faster if we do it in python. Initial undercloud install - 3.5x faster ---------------------------------------- Run deployment UndercloudPostDeployment ---- 130.50s < Shell Run deployment UndercloudPostDeployment ---- 37.39s < Python Re-Running undercloud install - 10x faster ------------------------------------------ Run deployment UndercloudPostDeployment ---- 405.01s < Shell Run deployment UndercloudPostDeployment ---- 39.95s < Python Change-Id: If7b3ad701e434ed0d606356b9bbab2716d53c5bb
This commit is contained in:
parent
86e79e047e
commit
0312050421
198
extraconfig/post_deploy/undercloud_post.py
Normal file
198
extraconfig/post_deploy/undercloud_post.py
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import openstack
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from keystoneauth1 import session
|
||||||
|
from keystoneauth1 import exceptions as ks_exceptions
|
||||||
|
import keystoneauth1.identity.generic as ks_auth
|
||||||
|
from mistralclient.api import client as mistralclient
|
||||||
|
from mistralclient.api import base as mistralclient_exc
|
||||||
|
|
||||||
|
|
||||||
|
AUTH_URL = os.environ['auth_url']
|
||||||
|
ADMIN_PASSWORD = os.environ['admin_password']
|
||||||
|
CONF = json.loads(os.environ['config'])
|
||||||
|
KS_AUTH = {'auth_url': AUTH_URL,
|
||||||
|
'project_name': 'admin',
|
||||||
|
'username': 'admin',
|
||||||
|
'password': ADMIN_PASSWORD,
|
||||||
|
'project_domain_name': 'Default',
|
||||||
|
'user_domain_name': 'Default'}
|
||||||
|
WORKBOOK_PATH = '/usr/share/openstack-tripleo-common/workbooks'
|
||||||
|
THT_DIR = '/usr/share/openstack-tripleo-heat-templates'
|
||||||
|
|
||||||
|
|
||||||
|
def _run_command(args, env=None, name=None):
|
||||||
|
"""Run the command defined by args and return its output
|
||||||
|
|
||||||
|
:param args: List of arguments for the command to be run.
|
||||||
|
:param env: Dict defining the environment variables. Pass None to use
|
||||||
|
the current environment.
|
||||||
|
:param name: User-friendly name for the command being run. A value of
|
||||||
|
None will cause args[0] to be used.
|
||||||
|
"""
|
||||||
|
if name is None:
|
||||||
|
name = args[0]
|
||||||
|
|
||||||
|
if env is None:
|
||||||
|
env = os.environ
|
||||||
|
env = env.copy()
|
||||||
|
|
||||||
|
# When running a localized python script, we need to tell it that we're
|
||||||
|
# using utf-8 for stdout, otherwise it can't tell because of the pipe.
|
||||||
|
env['PYTHONIOENCODING'] = 'utf8'
|
||||||
|
|
||||||
|
try:
|
||||||
|
return subprocess.check_output(args,
|
||||||
|
stderr=subprocess.STDOUT,
|
||||||
|
env=env).decode('utf-8')
|
||||||
|
except subprocess.CalledProcessError as ex:
|
||||||
|
print('ERROR: %s failed: %s' % (name, ex.output))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def _configure_nova(sdk):
|
||||||
|
""" Disable nova quotas """
|
||||||
|
sdk.set_compute_quotas('admin', cores='-1', instances='-1', ram='-1')
|
||||||
|
|
||||||
|
# Configure flavors.
|
||||||
|
sizings = {'ram': 4096, 'vcpus': 1, 'disk': 40}
|
||||||
|
extra_specs = {'resources:CUSTOM_BAREMETAL': 1,
|
||||||
|
'resources:VCPU': 0,
|
||||||
|
'resources:MEMORY_MB': 1,
|
||||||
|
'resources:DISK_GB': 0,
|
||||||
|
'capabilities:boot_option': 'local'}
|
||||||
|
profiles = ['control', 'compute', 'ceph-storage', 'block-storage',
|
||||||
|
'swift-storage']
|
||||||
|
flavors = [flavor.name for flavor in sdk.list_flavors()]
|
||||||
|
if 'baremetal' not in flavors:
|
||||||
|
flavor = sdk.create_flavor('baremetal', **sizings)
|
||||||
|
sdk.set_flavor_specs(flavor.id, extra_specs)
|
||||||
|
for profile in profiles:
|
||||||
|
if profile not in flavors:
|
||||||
|
flavor = sdk.create_flavor(profile, **sizings)
|
||||||
|
extra_specs.update({'capabilities:profile': profile})
|
||||||
|
sdk.set_flavor_specs(flavor.id, extra_specs)
|
||||||
|
print('INFO: Undercloud Post - Nova configuration completed successfully.')
|
||||||
|
|
||||||
|
|
||||||
|
def _create_default_keypair(sdk):
|
||||||
|
""" Set up a default keypair. """
|
||||||
|
ssh_dir = os.path.join(CONF['home_dir'], '.ssh')
|
||||||
|
public_key_file = os.path.join(ssh_dir, 'id_rsa.pub')
|
||||||
|
if (not [True for kp in sdk.compute.keypairs() if kp.name == 'default'] and
|
||||||
|
os.path.isfile(public_key_file)):
|
||||||
|
with open(public_key_file, 'r') as pub_key_file:
|
||||||
|
sdk.compute.create_keypair(name='default',
|
||||||
|
public_key=pub_key_file.read())
|
||||||
|
|
||||||
|
|
||||||
|
def _configure_wrokbooks_and_workflows(mistral):
|
||||||
|
for workbook in [w for w in mistral.workbooks.list()
|
||||||
|
if w.name.startswith('tripleo')]:
|
||||||
|
mistral.workbooks.delete(workbook.name)
|
||||||
|
managed_tag = 'tripleo-common-managed'
|
||||||
|
all_workflows = mistral.workflows.list()
|
||||||
|
workflows_delete = [w.name for w in all_workflows
|
||||||
|
if managed_tag in w.tags]
|
||||||
|
# in order to delete workflows they should have no triggers associated
|
||||||
|
for trigger in [t for t in mistral.cron_triggers.list()
|
||||||
|
if t.workflow_name in workflows_delete]:
|
||||||
|
mistral.cron_triggers.delete(trigger.name)
|
||||||
|
for workflow_name in workflows_delete:
|
||||||
|
mistral.workflows.delete(workflow_name)
|
||||||
|
for workbook in [f for f in os.listdir(WORKBOOK_PATH)
|
||||||
|
if os.path.isfile(os.path.join(WORKBOOK_PATH, f))]:
|
||||||
|
mistral.workbooks.create(os.path.join(WORKBOOK_PATH, workbook))
|
||||||
|
print('INFO: Undercloud post - Mistral workbooks configured successfully.')
|
||||||
|
|
||||||
|
|
||||||
|
def _create_logging_cron(mistral):
|
||||||
|
mistral.cron_triggers.create(
|
||||||
|
'publish-ui-logs-hourly',
|
||||||
|
'tripleo.plan_management.v1.publish_ui_logs_to_swift',
|
||||||
|
pattern='0 * * * *')
|
||||||
|
print('INFO: Undercloud post - Cron triggers configured successfully.')
|
||||||
|
|
||||||
|
|
||||||
|
def _store_snmp_password_in_mistral_env(mistral):
|
||||||
|
""" Store the SNMP password in a mistral environment """
|
||||||
|
env_name = 'tripleo.undercloud-config'
|
||||||
|
config_data = {
|
||||||
|
'undercloud_ceilometer_snmpd_password':
|
||||||
|
CONF['snmp_readonly_user_password']
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
mistral.environments.get(env_name).variables
|
||||||
|
mistral.environments.update(
|
||||||
|
name=env_name,
|
||||||
|
description='Undercloud configuration parameters',
|
||||||
|
variables=json.dumps(config_data, sort_keys=True))
|
||||||
|
except (ks_exceptions.NotFound, mistralclient_exc.APIException):
|
||||||
|
# The environment is not created, we need to create it
|
||||||
|
mistral.environments.create(
|
||||||
|
name=env_name,
|
||||||
|
description='Undercloud configuration parameters',
|
||||||
|
variables=json.dumps(config_data, sort_keys=True))
|
||||||
|
print('INFO: Undercloud post - Mistral environment configured '
|
||||||
|
'successfully.')
|
||||||
|
|
||||||
|
|
||||||
|
def _prepare_ssh_environment(mistral):
|
||||||
|
mistral.executions.create('tripleo.validations.v1.copy_ssh_key')
|
||||||
|
|
||||||
|
|
||||||
|
def _upload_validations_to_swift(mistral):
|
||||||
|
mistral.executions.create('tripleo.validations.v1.upload_validations')
|
||||||
|
|
||||||
|
|
||||||
|
def _create_default_plan(mistral):
|
||||||
|
plan_exists = [True for c in sdk.list_containers() if
|
||||||
|
c['name'] == 'overcloud']
|
||||||
|
if not plan_exists and os.path.isdir(THT_DIR):
|
||||||
|
mistral.executions.create(
|
||||||
|
'tripleo.plan_management.v1.create_deployment_plan',
|
||||||
|
workflow_input={'container': 'overcloud',
|
||||||
|
'use_default_templates': True})
|
||||||
|
print('INFO: Undercloud post - Default plan overcloud created.')
|
||||||
|
|
||||||
|
|
||||||
|
nova_api_enabled = 'true' in _run_command(
|
||||||
|
['hiera', 'nova_api_enabled']).lower()
|
||||||
|
mistral_api_enabled = 'true' in _run_command(
|
||||||
|
['hiera','mistral_api_enabled']).lower()
|
||||||
|
tripleo_validations_enabled = 'true' in _run_command(
|
||||||
|
['hiera', 'tripleo_validations_enabled']).lower()
|
||||||
|
|
||||||
|
if not nova_api_enabled:
|
||||||
|
print('WARNING: Undercloud Post - Nova API is disabled.')
|
||||||
|
if not mistral_api_enabled:
|
||||||
|
print('WARNING: Undercloud Post - Mistral API is disabled.')
|
||||||
|
if not tripleo_validations_enabled:
|
||||||
|
print('WARNING: Undercloud Post - Tripleo validations is disabled.')
|
||||||
|
|
||||||
|
sdk = openstack.connect(**KS_AUTH)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if nova_api_enabled:
|
||||||
|
_configure_nova(sdk)
|
||||||
|
_create_default_keypair(sdk)
|
||||||
|
if mistral_api_enabled:
|
||||||
|
mistral = mistralclient.client(
|
||||||
|
mistral_url=sdk.workflow.get_endpoint(),
|
||||||
|
session=session.Session(auth=ks_auth.Password(**KS_AUTH)))
|
||||||
|
_configure_wrokbooks_and_workflows(mistral)
|
||||||
|
_create_logging_cron(mistral)
|
||||||
|
_store_snmp_password_in_mistral_env(mistral)
|
||||||
|
_create_default_plan(mistral)
|
||||||
|
if tripleo_validations_enabled:
|
||||||
|
_prepare_ssh_environment(mistral)
|
||||||
|
_upload_validations_to_swift(mistral)
|
||||||
|
print('INFO: Undercloud post - Validations execututed and '
|
||||||
|
'uploaded to Swift.')
|
||||||
|
except Exception:
|
||||||
|
print('ERROR: Undercloud Post - Failed.')
|
||||||
|
raise
|
@ -6,7 +6,6 @@ ln -sf /etc/puppet/hiera.yaml /etc/hiera.yaml
|
|||||||
HOMEDIR="$homedir"
|
HOMEDIR="$homedir"
|
||||||
USERNAME=`ls -ld $HOMEDIR | awk {'print $3'}`
|
USERNAME=`ls -ld $HOMEDIR | awk {'print $3'}`
|
||||||
GROUPNAME=`ls -ld $HOMEDIR | awk {'print $4'}`
|
GROUPNAME=`ls -ld $HOMEDIR | awk {'print $4'}`
|
||||||
THT_DIR="/usr/share/openstack-tripleo-heat-templates"
|
|
||||||
|
|
||||||
# WRITE OUT STACKRC
|
# WRITE OUT STACKRC
|
||||||
touch $HOMEDIR/stackrc
|
touch $HOMEDIR/stackrc
|
||||||
@ -70,87 +69,3 @@ if ! grep "$(cat $HOMEDIR/.ssh/id_rsa.pub)" $HOMEDIR/.ssh/authorized_keys; then
|
|||||||
cat $HOMEDIR/.ssh/id_rsa.pub >> $HOMEDIR/.ssh/authorized_keys
|
cat $HOMEDIR/.ssh/id_rsa.pub >> $HOMEDIR/.ssh/authorized_keys
|
||||||
fi
|
fi
|
||||||
chown -R "$USERNAME:$GROUPNAME" "$HOMEDIR/.ssh"
|
chown -R "$USERNAME:$GROUPNAME" "$HOMEDIR/.ssh"
|
||||||
|
|
||||||
if [ "$(hiera nova_api_enabled)" = "true" ]; then
|
|
||||||
# Disable nova quotas
|
|
||||||
openstack quota set --cores -1 --instances -1 --ram -1 $(openstack project show admin | awk '$2=="id" {print $4}')
|
|
||||||
|
|
||||||
# Configure flavors.
|
|
||||||
RESOURCES='--property resources:CUSTOM_BAREMETAL=1 --property resources:DISK_GB=0 --property resources:MEMORY_MB=0 --property resources:VCPU=0 --property capabilities:boot_option=local'
|
|
||||||
SIZINGS='--ram 4096 --vcpus 1 --disk 40'
|
|
||||||
|
|
||||||
if ! openstack flavor show baremetal >/dev/null 2>&1; then
|
|
||||||
openstack flavor create $SIZINGS $RESOURCES baremetal
|
|
||||||
fi
|
|
||||||
if ! openstack flavor show control >/dev/null 2>&1; then
|
|
||||||
openstack flavor create $SIZINGS $RESOURCES --property capabilities:profile=control control
|
|
||||||
fi
|
|
||||||
if ! openstack flavor show compute >/dev/null 2>&1; then
|
|
||||||
openstack flavor create $SIZINGS $RESOURCES --property capabilities:profile=compute compute
|
|
||||||
fi
|
|
||||||
if ! openstack flavor show ceph-storage >/dev/null 2>&1; then
|
|
||||||
openstack flavor create $SIZINGS $RESOURCES --property capabilities:profile=ceph-storage ceph-storage
|
|
||||||
fi
|
|
||||||
if ! openstack flavor show block-storage >/dev/null 2>&1; then
|
|
||||||
openstack flavor create $SIZINGS $RESOURCES --property capabilities:profile=block-storage block-storage
|
|
||||||
fi
|
|
||||||
if ! openstack flavor show swift-storage >/dev/null 2>&1; then
|
|
||||||
openstack flavor create $SIZINGS $RESOURCES --property capabilities:profile=swift-storage swift-storage
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Set up a default keypair.
|
|
||||||
if [ ! -e $HOMEDIR/.ssh/id_rsa ]; then
|
|
||||||
sudo -E -u $USERNAME ssh-keygen -t rsa -N '' -f $HOMEDIR/.ssh/id_rsa
|
|
||||||
fi
|
|
||||||
|
|
||||||
if openstack keypair show default; then
|
|
||||||
echo Keypair already exists.
|
|
||||||
else
|
|
||||||
echo Creating new keypair.
|
|
||||||
openstack keypair create --public-key $HOMEDIR/.ssh/id_rsa.pub 'default'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# MISTRAL WORKFLOW CONFIGURATION
|
|
||||||
if [ "$(hiera mistral_api_enabled)" = "true" ]; then
|
|
||||||
echo Configuring Mistral workbooks.
|
|
||||||
for workbook in $(openstack workbook list | grep tripleo | cut -f 2 -d ' '); do
|
|
||||||
openstack workbook delete $workbook
|
|
||||||
done
|
|
||||||
if openstack cron trigger show publish-ui-logs-hourly >/dev/null 2>&1; then
|
|
||||||
openstack cron trigger delete publish-ui-logs-hourly
|
|
||||||
fi
|
|
||||||
|
|
||||||
for workflow in $(openstack workflow list -c Name -f value --filter tags=tripleo-common-managed); do
|
|
||||||
openstack workflow delete $workflow
|
|
||||||
done
|
|
||||||
|
|
||||||
for workbook in $(ls /usr/share/openstack-tripleo-common/workbooks/*); do
|
|
||||||
openstack workbook create $workbook
|
|
||||||
done
|
|
||||||
openstack cron trigger create publish-ui-logs-hourly tripleo.plan_management.v1.publish_ui_logs_to_swift --pattern '0 * * * *'
|
|
||||||
echo Mistral workbooks configured successfully.
|
|
||||||
|
|
||||||
# Store the SNMP password in a mistral environment
|
|
||||||
if ! openstack workflow env show tripleo.undercloud-config >/dev/null 2>&1; then
|
|
||||||
TMP_MISTRAL_ENV=$(mktemp)
|
|
||||||
echo "{\"name\": \"tripleo.undercloud-config\", \"variables\": {\"undercloud_ceilometer_snmpd_password\": \"$snmp_readonly_user_password\"}}" > $TMP_MISTRAL_ENV
|
|
||||||
echo Configure Mistral environment with undercloud-config
|
|
||||||
openstack workflow env create $TMP_MISTRAL_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Create the default deployment plan from /usr/share/openstack-tripleo-heat-templates
|
|
||||||
# but only if there is no overcloud container in swift yet.
|
|
||||||
if [ -d "$THT_DIR" ] && ! openstack container list -c Name -f value | grep -qe "^overcloud$"; then
|
|
||||||
echo Create default deployment plan
|
|
||||||
openstack workflow execution create tripleo.plan_management.v1.create_deployment_plan '{"container": "overcloud", "use_default_templates": true}'
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$(hiera tripleo_validations_enabled)" = "true" ]; then
|
|
||||||
echo Execute copy_ssh_key validations
|
|
||||||
openstack workflow execution create tripleo.validations.v1.copy_ssh_key
|
|
||||||
|
|
||||||
echo Upload validations to Swift
|
|
||||||
openstack workflow execution create tripleo.validations.v1.upload_validations
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
@ -98,7 +98,6 @@ resources:
|
|||||||
- name: deploy_identifier
|
- name: deploy_identifier
|
||||||
- name: admin_password
|
- name: admin_password
|
||||||
- name: auth_url
|
- name: auth_url
|
||||||
- name: snmp_readonly_user_password
|
|
||||||
- name: internal_tls_ca_file
|
- name: internal_tls_ca_file
|
||||||
config: {get_file: ./undercloud_post.sh}
|
config: {get_file: ./undercloud_post.sh}
|
||||||
|
|
||||||
@ -112,7 +111,6 @@ resources:
|
|||||||
ssl_certificate: {get_param: SSLCertificate}
|
ssl_certificate: {get_param: SSLCertificate}
|
||||||
homedir: {get_param: UndercloudHomeDir}
|
homedir: {get_param: UndercloudHomeDir}
|
||||||
admin_password: {get_param: AdminPassword}
|
admin_password: {get_param: AdminPassword}
|
||||||
snmp_readonly_user_password: {get_param: SnmpdReadonlyUserPassword}
|
|
||||||
internal_tls_ca_file:
|
internal_tls_ca_file:
|
||||||
if:
|
if:
|
||||||
- ca_file_enabled
|
- ca_file_enabled
|
||||||
@ -133,6 +131,47 @@ resources:
|
|||||||
port: 5000
|
port: 5000
|
||||||
path: /
|
path: /
|
||||||
|
|
||||||
|
UndercloudPostPyConfig:
|
||||||
|
type: OS::Heat::SoftwareConfig
|
||||||
|
properties:
|
||||||
|
group: script
|
||||||
|
inputs:
|
||||||
|
- name: admin_password
|
||||||
|
- name: auth_url
|
||||||
|
- name: config
|
||||||
|
config: {get_file: ./undercloud_post.py}
|
||||||
|
|
||||||
|
UndercloudPostPyDeployment:
|
||||||
|
type: OS::Heat::SoftwareDeployments
|
||||||
|
depends_on: UndercloudPostDeployment
|
||||||
|
properties:
|
||||||
|
name: UndercloudPostPyDeployment
|
||||||
|
servers: {get_param: servers}
|
||||||
|
config: {get_resource: UndercloudPostPyConfig}
|
||||||
|
input_values:
|
||||||
|
admin_password: {get_param: AdminPassword}
|
||||||
|
auth_url:
|
||||||
|
if:
|
||||||
|
- tls_enabled
|
||||||
|
- make_url:
|
||||||
|
scheme: https
|
||||||
|
host: {get_param: [DeployedServerPortMap, 'public_virtual_ip', fixed_ips, 0, ip_address]}
|
||||||
|
port: 13000
|
||||||
|
path: /
|
||||||
|
- make_url:
|
||||||
|
scheme: http
|
||||||
|
host: {get_param: [DeployedServerPortMap, 'control_virtual_ip', fixed_ips, 0, ip_address]}
|
||||||
|
port: 5000
|
||||||
|
path: /
|
||||||
|
config:
|
||||||
|
str_replace:
|
||||||
|
template: JSON
|
||||||
|
params:
|
||||||
|
JSON:
|
||||||
|
home_dir: {get_param: UndercloudHomeDir}
|
||||||
|
snmp_readonly_user_password: {get_param: SnmpdReadonlyUserPassword}
|
||||||
|
|
||||||
|
|
||||||
UndercloudCtlplaneNetworkConfig:
|
UndercloudCtlplaneNetworkConfig:
|
||||||
type: OS::Heat::SoftwareConfig
|
type: OS::Heat::SoftwareConfig
|
||||||
properties:
|
properties:
|
||||||
|
Loading…
Reference in New Issue
Block a user