From 0312050421c3174c9641b4355b48ab78849de6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Harald=20Jens=C3=A5s?= Date: Wed, 31 Oct 2018 16:09:45 +0100 Subject: [PATCH] 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 --- extraconfig/post_deploy/undercloud_post.py | 198 +++++++++++++++++++ extraconfig/post_deploy/undercloud_post.sh | 85 -------- extraconfig/post_deploy/undercloud_post.yaml | 43 +++- 3 files changed, 239 insertions(+), 87 deletions(-) create mode 100644 extraconfig/post_deploy/undercloud_post.py diff --git a/extraconfig/post_deploy/undercloud_post.py b/extraconfig/post_deploy/undercloud_post.py new file mode 100644 index 0000000000..b1b8426864 --- /dev/null +++ b/extraconfig/post_deploy/undercloud_post.py @@ -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 diff --git a/extraconfig/post_deploy/undercloud_post.sh b/extraconfig/post_deploy/undercloud_post.sh index 6139bd9437..f5a3954561 100755 --- a/extraconfig/post_deploy/undercloud_post.sh +++ b/extraconfig/post_deploy/undercloud_post.sh @@ -6,7 +6,6 @@ ln -sf /etc/puppet/hiera.yaml /etc/hiera.yaml HOMEDIR="$homedir" USERNAME=`ls -ld $HOMEDIR | awk {'print $3'}` GROUPNAME=`ls -ld $HOMEDIR | awk {'print $4'}` -THT_DIR="/usr/share/openstack-tripleo-heat-templates" # WRITE OUT 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 fi 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 diff --git a/extraconfig/post_deploy/undercloud_post.yaml b/extraconfig/post_deploy/undercloud_post.yaml index 0b2bb6dd56..a736e9f1b1 100644 --- a/extraconfig/post_deploy/undercloud_post.yaml +++ b/extraconfig/post_deploy/undercloud_post.yaml @@ -98,7 +98,6 @@ resources: - name: deploy_identifier - name: admin_password - name: auth_url - - name: snmp_readonly_user_password - name: internal_tls_ca_file config: {get_file: ./undercloud_post.sh} @@ -112,7 +111,6 @@ resources: ssl_certificate: {get_param: SSLCertificate} homedir: {get_param: UndercloudHomeDir} admin_password: {get_param: AdminPassword} - snmp_readonly_user_password: {get_param: SnmpdReadonlyUserPassword} internal_tls_ca_file: if: - ca_file_enabled @@ -133,6 +131,47 @@ resources: port: 5000 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: type: OS::Heat::SoftwareConfig properties: