Merge "Enhance Shaker to support basic heat environment files"

This commit is contained in:
Zuul 2018-10-30 09:51:57 +00:00 committed by Gerrit Code Review
commit 296cfb1344
8 changed files with 234 additions and 4 deletions

View File

@ -302,10 +302,14 @@ class Deployment(object):
exit(1) exit(1)
merged_parameters.update(specification.get('template_parameters', {})) merged_parameters.update(specification.get('template_parameters', {}))
env_file = specification.get('env_file', None)
if env_file is not None:
env_file = self._render_env_template(env_file, base_dir)
self.has_stack = True self.has_stack = True
stack_id = heat.create_stack( stack_id = heat.create_stack(
self.openstack_client.heat, self.stack_name, rendered_template, self.openstack_client.heat, self.stack_name, rendered_template,
merged_parameters) merged_parameters, env_file)
# get info about deployed objects # get info about deployed objects
outputs = heat.get_stack_outputs(self.openstack_client.heat, stack_id) outputs = heat.get_stack_outputs(self.openstack_client.heat, stack_id)
@ -330,6 +334,20 @@ class Deployment(object):
return functools.partial(override_ip, return functools.partial(override_ip,
ip_type=override_spec.get('ip')) ip_type=override_spec.get('ip'))
# translate jinja decorations in env files
def _render_env_template(self, env_file, base_dir):
env_template = utils.read_file(env_file,
base_dir=base_dir)
env_values = {
'CONF': cfg.CONF
}
compiled_env = jinja2.Template(env_template)
rendered_env = compiled_env.render(env_values)
environment = utils.read_yaml(rendered_env)
return environment
def deploy(self, deployment, base_dir=None, server_endpoint=None): def deploy(self, deployment, base_dir=None, server_endpoint=None):
agents = {} agents = {}

View File

@ -136,12 +136,16 @@ def write_file(data, file_name, base_dir=''):
def read_yaml_file(file_name): def read_yaml_file(file_name):
raw = read_file(file_name) raw = read_file(file_name)
return read_yaml(raw)
def read_yaml(raw):
try: try:
parsed = yaml.safe_load(raw) parsed = yaml.safe_load(raw)
return parsed return parsed
except Exception as e: except Exception as e:
LOG.error('Failed to parse file %(file)s in YAML format: %(err)s', LOG.error('Failed to parse input %(yaml)s in YAML format: %(err)s',
dict(file=file_name, err=e)) dict(yaml=raw, err=e))
raise raise

View File

@ -22,11 +22,12 @@ from oslo_log import log as logging
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
def create_stack(heat_client, stack_name, template, parameters): def create_stack(heat_client, stack_name, template, parameters, environment):
stack_params = { stack_params = {
'stack_name': stack_name, 'stack_name': stack_name,
'template': template, 'template': template,
'parameters': parameters, 'parameters': parameters,
'environment': environment,
} }
stack = heat_client.stacks.create(**stack_params)['stack'] stack = heat_client.stacks.create(**stack_params)['stack']

View File

@ -11,6 +11,8 @@ mapping:
mapping: mapping:
template: template:
type: str type: str
env_file:
type: str
agents: agents:
type: any type: any
accommodation: accommodation:

3
shaker/scenarios/test/env/sample.env vendored Normal file
View File

@ -0,0 +1,3 @@
parameters:
custom_cidr: '192.0.0.0/16'
agent_join_timeout: {{ CONF.agent_join_timeout }}

View File

@ -0,0 +1,112 @@
heat_template_version: 2013-05-23
description:
This Heat template creates a new Neutron network, a router to the external
network and plugs instances into this new network. All instances are located
in the same L2 domain.
parameters:
image:
type: string
description: Name of image to use for servers
flavor:
type: string
description: Flavor to use for servers
external_net:
type: string
description: ID or name of external network
server_endpoint:
type: string
description: Server endpoint address
dns_nameservers:
type: comma_delimited_list
description: DNS nameservers for the subnet
custom_cidr:
type: string
description: set a cidr from the env file
agent_join_timeout:
type: string
description: shake CONF setting for agent_join_timeout
resources:
private_net:
type: OS::Neutron::Net
properties:
name: {{ unique }}_net
private_subnet:
type: OS::Neutron::Subnet
properties:
network_id: { get_resource: private_net }
cidr: { get_param: custom_cidr }
dns_nameservers: { get_param: dns_nameservers }
router:
type: OS::Neutron::Router
properties:
external_gateway_info:
network: { get_param: external_net }
router_interface:
type: OS::Neutron::RouterInterface
properties:
router_id: { get_resource: router }
subnet_id: { get_resource: private_subnet }
server_security_group:
type: OS::Neutron::SecurityGroup
properties:
rules: [
{remote_ip_prefix: 0.0.0.0/0,
protocol: tcp,
port_range_min: 1,
port_range_max: 65535},
{remote_ip_prefix: 0.0.0.0/0,
protocol: udp,
port_range_min: 1,
port_range_max: 65535},
{remote_ip_prefix: 0.0.0.0/0,
protocol: icmp}]
{% for agent in agents.values() %}
{{ agent.id }}:
type: OS::Nova::Server
properties:
name: {{ agent.id }}
image: { get_param: image }
flavor: { get_param: flavor }
availability_zone: "{{ agent.availability_zone }}"
networks:
- port: { get_resource: {{ agent.id }}_port }
user_data_format: RAW
user_data:
str_replace:
template: |
#!/bin/sh
echo $AGENT_JOIN_TIMEOUT > /tmp/check_agent_timeout
screen -dmS shaker-agent-screen shaker-agent --server-endpoint=$SERVER_ENDPOINT --agent-id=$AGENT_ID
params:
"$SERVER_ENDPOINT": { get_param: server_endpoint }
"$AGENT_ID": {{ agent.id }}
"$AGENT_JOIN_TIMEOUT": { get_param: agent_join_timeout }
{{ agent.id }}_port:
type: OS::Neutron::Port
properties:
network_id: { get_resource: private_net }
fixed_ips:
- subnet_id: { get_resource: private_subnet }
security_groups: [{ get_resource: server_security_group }]
{% endfor %}
outputs:
{% for agent in agents.values() %}
{{ agent.id }}_instance_name:
value: { get_attr: [ {{ agent.id }}, instance_name ] }
{{ agent.id }}_ip:
value: { get_attr: [ {{ agent.id }}, networks, { get_attr: [private_net, name] }, 0 ] }
{% endfor %}

View File

@ -0,0 +1,23 @@
title: Sample TCP Test with Environment File
description:
This test definition demonstrates the use of an environment file.
In this scenario Shaker launches pairs of instances in the same tenant
network. Every instance is hosted on a separate compute node, 1 compute node
is utilized. The traffic goes within the tenant network (L2 domain)
deployment:
template: l2_with_env.hot
env_file: env/sample.env
accommodation: [pair, compute_nodes: 1]
execution:
tests:
-
title: tcp
class: iperf3
sla:
- "[type == 'agent'] >> (stats.bandwidth.avg > 5000)"

View File

@ -17,12 +17,15 @@ import collections
import copy import copy
import itertools import itertools
import mock import mock
import os
import re
import testtools import testtools
from oslo_config import cfg from oslo_config import cfg
from oslo_config import fixture as config_fixture_pkg from oslo_config import fixture as config_fixture_pkg
from shaker.engine import config from shaker.engine import config
from shaker.engine import deploy from shaker.engine import deploy
from shaker.engine import utils
from shaker.openstack.clients import nova from shaker.openstack.clients import nova
from shaker.tests import fakes from shaker.tests import fakes
@ -562,6 +565,70 @@ class TestDeploy(testtools.TestCase):
self.assertRaises(deploy.DeploymentException, self.assertRaises(deploy.DeploymentException,
deployment.deploy, {'template': 'foo'}) deployment.deploy, {'template': 'foo'})
@mock.patch('shaker.openstack.clients.heat.get_stack_outputs')
@mock.patch('shaker.openstack.clients.heat.create_stack')
@mock.patch('shaker.openstack.clients.openstack.OpenStackClient')
@mock.patch('shaker.engine.deploy.Deployment._get_compute_nodes')
def test_deploy_from_hot_with_env_file(self, nova_nodes_mock,
openstack_mock, create_stack_mock,
stack_output_mock):
test_file = 'shaker/scenarios/test/sample_with_env.yaml'
absolute_path = utils.resolve_relative_path(test_file)
scenario = utils.read_yaml_file(absolute_path)
heat_id = 'shaker_abcdefg'
server_endpoint = "127.0.0.01"
base_dir = os.path.dirname(absolute_path)
deployment = deploy.Deployment()
deployment.stack_name = heat_id
deployment.external_net = 'test-external_net'
deployment.image_name = 'test-image'
deployment.flavor_name = 'test-flavor'
deployment.dns_nameservers = '8.8.8.8'
deployment.openstack_client = openstack_mock
# read the env file to determine what cidr is set to
# minus the last digit
env_file = utils.read_file(scenario['deployment']['env_file'],
base_dir)
cidr = re.findall(r'[0-9]+(?:\.[0-9]+){2}', env_file)[0]
nova_nodes_mock.return_value = [{'host': 'host-1', 'zone': 'nova'}]
create_stack_mock.create_stack.return_value = heat_id
heat_outputs = {
heat_id + '_master_0_instance_name': 'instance-0000052f',
heat_id + '_master_0_ip': '192.0.0.3',
heat_id + '_slave_0_ip': '192.0.0.4',
heat_id + '_slave_0_instance_name': 'instance-0000052c'}
stack_output_mock.return_value = heat_outputs
expected = {
'shaker_abcdefg_master_0': {'availability_zone': 'nova:host-1',
'id': 'shaker_abcdefg_master_0',
'ip': cidr + '.3',
'mode': 'master',
'node': 'host-1',
'slave_id': 'shaker_abcdefg_slave_0',
'zone': 'nova'},
'shaker_abcdefg_slave_0': {'availability_zone': 'nova:host-1',
'id': 'shaker_abcdefg_slave_0',
'ip': cidr + '.4',
'master_id': 'shaker_abcdefg_master_0',
'mode': 'slave',
'node': 'host-1',
'zone': 'nova'}}
agents = deployment._deploy_from_hot(scenario['deployment'],
server_endpoint,
base_dir=base_dir)
self.assertEqual(expected, agents)
@mock.patch('shaker.openstack.clients.openstack.OpenStackClient') @mock.patch('shaker.openstack.clients.openstack.OpenStackClient')
def test_get_compute_nodes_flavor_no_extra_specs(self, def test_get_compute_nodes_flavor_no_extra_specs(self,
nova_client_mock): nova_client_mock):