Adds in-instance hook for saltstack

An in-instance hook which allows salt to be used for provisioning
software on instances using SoftwareConfig/StructuredConfig and
SoftwareDeployment resources.

Change-Id: Id5b2a63576d611cd33623db6a68813bc93882538
Closes-Bug: #1320727
This commit is contained in:
Rabi Mishra 2014-06-25 18:08:37 +05:30
parent c9b682f1ef
commit 75d7a881b3
6 changed files with 228 additions and 0 deletions

View File

@ -35,6 +35,7 @@ with the following:
os-apply-config \
heat-config-cfn-init \
heat-config-puppet \
heat-config-salt \
heat-config-script \
-o fedora-software-config.qcow2
glance image-create --disk-format qcow2 --container-format bare --name fedora-software-config < \

View File

@ -0,0 +1,3 @@
A hook which uses salt library to apply the provided configuration
as a state. Config inputs are passed as opts and output values are
read from the yaml returned.

View File

@ -0,0 +1,8 @@
#!/bin/bash
set -x
SCRIPTDIR=$(dirname $0)
install-packages salt-minion
install -D -g root -o root -m 0755 ${SCRIPTDIR}/hook-salt.py /var/lib/heat-config/hooks/salt

View File

@ -0,0 +1,99 @@
#!/usr/bin/env python
import json
import logging
import os
import salt.cli
import salt.config
import sys
import yaml
from salt.exceptions import SaltInvocationError
WORKING_DIR = os.environ.get('HEAT_SALT_WORKING',
'/var/lib/heat-config/heat-config-salt')
SALT_MINION_CONFIG = os.environ.get('SALT_MINION_CONFIG',
'/etc/salt/minion')
def prepare_dir(path):
if not os.path.isdir(path):
os.makedirs(path, 0o700)
def main(argv=sys.argv):
log = logging.getLogger('heat-config')
handler = logging.StreamHandler(sys.stderr)
handler.setFormatter(
logging.Formatter(
'[%(asctime)s] (%(name)s) [%(levelname)s] %(message)s'))
log.addHandler(handler)
log.setLevel('DEBUG')
prepare_dir(WORKING_DIR)
os.chdir(WORKING_DIR)
c = json.load(sys.stdin)
opts = salt.config.minion_config(SALT_MINION_CONFIG)
opts['file_roots'] = {'base': [WORKING_DIR]}
opts['file_client'] = 'local'
opts['local'] = 'local'
opts['fun'] = 'state.sls'
opts['arg'] = [c['id']]
for input in c['inputs']:
key = input['name']
opts[key] = input.get('value', '')
state_file = '%s.sls' % c['id']
config = c.get('config', '')
if isinstance(config, dict):
yaml_config = yaml.safe_dump(config, default_flow_style=False)
else:
yaml_config = config
fn = os.path.join(WORKING_DIR, state_file)
with os.fdopen(os.open(fn, os.O_CREAT | os.O_WRONLY, 0o700), 'w') as f:
f.write(yaml_config)
caller = salt.cli.caller.Caller(opts)
log.debug('Applying Salt state %s' % state_file)
stdout, stderr = None, None
try:
ret = caller.call()
except SaltInvocationError as err:
log.error(
'Salt invocation error while applying Salt sate %s' % state_file)
stderr = err
log.info('Return code %s' % ret['retcode'])
# returncode of 0 means there were successfull changes
if ret['retcode'] == 0:
log.info('Completed applying salt state %s' % state_file)
stdout = ret
else:
log.error('Error applying Salt state %s. [%s]\n'
% (state_file, ret['retcode']))
stderr = ret
response = {}
for output in c.get('outputs') or []:
output_name = output['name']
response[output_name] = ret[output_name]
response.update({
'deploy_stdout': stdout,
'deploy_stderr': stderr,
'deploy_status_code': ret['retcode'],
})
json.dump(response, sys.stdout)
if __name__ == '__main__':
sys.exit(main(sys.argv))

View File

@ -0,0 +1,7 @@
testsls:
pkg.installed:
{% if grains['os_family'] == 'RedHat' %}
- name: {{ pillar['master']['pkg-redhat'] }}
{% elif grains['os_family'] == 'Debian' %}
- name: {{ pillar['master']['pkg-apache'] }}
{% endif %}

View File

@ -0,0 +1,110 @@
heat_template_version: 2013-05-23
parameters:
key_name:
type: string
flavor:
type: string
default: m1.small
image:
type: string
default: fedora-software-config
resources:
the_sg:
type: OS::Neutron::SecurityGroup
properties:
name: the_sg
description: Ping and SSH
rules:
- protocol: icmp
- protocol: tcp
port_range_min: 22
port_range_max: 22
config:
type: OS::Heat::SoftwareConfig
properties:
group: salt
inputs:
- name: pkg-redhat
- name: pkg-debian
outputs:
- name: out
config:
get_file: config-scripts/example-salt-state.sls
structured_config:
type: OS::Heat::StructuredConfig
properties:
group: salt
inputs:
- name: pkg-redhat
- name: svc-redhat
config:
inline_sls:
pkg.installed:
- name: {get_input: pkg-redhat}
service.running:
- name: {get_input: svc-redhat}
- require:
- pkg: {get_input: pkg-redhat}
deployment:
type: OS::Heat::SoftwareDeployment
properties:
config:
get_resource: config
server:
get_resource: server
input_values:
pkg-redhat: httpd
pkg-debian: apache2
other_deployment:
type: OS::Heat::SoftwareDeployment
properties:
config:
get_resource: config
server:
get_resource: server
input_values:
pkg-redhat: mariadb-server
pkg-debian: mysql-server
structured_deployment:
type: OS::Heat::StructuredDeployment
properties:
config:
get_resource: structured_config
server:
get_resource: server
input_values:
pkg-redhat: mariadb-server
svc-redhat: mysqld
server:
type: OS::Nova::Server
properties:
image: {get_param: image}
flavor: {get_param: flavor}
key_name: {get_param: key_name}
security_groups:
- {get_resource: the_sg}
user_data_format: SOFTWARE_CONFIG
outputs:
status_code_deployment:
value:
get_attr: [deployment, deploy_status_code]
status_code_other_deployment:
value:
get_attr: [other_deployment, deploy_status_code]
status_code_structured_deployment:
value:
get_attr: [structured_deployment, deploy_status_code]
stdout:
value:
get_attr: [deployment, deploy_stdout]
stderr:
value:
get_attr: [deployment, deploy_stderr]