Externalization of heat template
Reasons: preparing trove to support multi-datastores Changes: template loading, heat-support update This code refactors the earlier code of instance creation using heat. - Configuration templates stored at trove/templates/(datastore_type)/config.template - Heat templates are stored in trove/templates/(datastore_type)/heat.template - One template per datastore type - New parameter - ImageId - Removed SSH key parameter - All resources are updated according to available OS resources (for more details please visit heat repository). Author: Denis Makogon (dmakogon) <dmakogon@mirantis.com> Co-Authored-By: Nilakhya Chatterjee (SnowDust) <nilakhya.chatterjee@globallogic.com> Change-Id: Ie6863a625e1525e152ed2886a630dc4f233be8dc Implements: blueprint provide-custom-heat-templates-for-each-service-in-trove
This commit is contained in:
committed by
Nilakhya
parent
12f2b0d47c
commit
c3d2466f76
@@ -77,6 +77,9 @@ agent_call_high_timeout = 150
|
||||
# Whether to use nova's contrib api for create server with volume
|
||||
use_nova_server_volume = False
|
||||
|
||||
# Datastore templates
|
||||
template_path = /etc/trove/templates/
|
||||
|
||||
# ============ notifer queue kombu connection options ========================
|
||||
|
||||
notifier_queue_hostname = localhost
|
||||
|
||||
@@ -216,6 +216,9 @@ common_opts = [
|
||||
help='Extention for default service managers.'
|
||||
' Allows to use custom managers for each of'
|
||||
' service type supported in trove'),
|
||||
cfg.StrOpt('template_path',
|
||||
default='/etc/trove/templates/',
|
||||
help='Path which leads to datastore templates'),
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
@@ -13,20 +13,27 @@
|
||||
# under the License.
|
||||
|
||||
import jinja2
|
||||
from trove.common import cfg
|
||||
from trove.common import exception
|
||||
from trove.openstack.common import log as logging
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
ENV = jinja2.Environment(loader=jinja2.ChoiceLoader([
|
||||
jinja2.FileSystemLoader("/etc/trove/templates"),
|
||||
jinja2.PackageLoader("trove", "templates")
|
||||
jinja2.FileSystemLoader(CONF.template_path),
|
||||
jinja2.PackageLoader("trove", "templates"),
|
||||
]))
|
||||
|
||||
|
||||
class SingleInstanceConfigTemplate(object):
|
||||
""" This class selects a single configuration file by database type for
|
||||
rendering on the guest """
|
||||
def __init__(self, service_type, flavor_dict, instance_id):
|
||||
|
||||
def __init__(self, datastore_type, flavor_dict, instance_id):
|
||||
""" Constructor
|
||||
|
||||
:param service_type: The database type.
|
||||
:param datastore_type: The database type.
|
||||
:type name: str.
|
||||
:param flavor_dict: dict containing flavor details for use in jinja.
|
||||
:type flavor_dict: dict.
|
||||
@@ -35,7 +42,7 @@ class SingleInstanceConfigTemplate(object):
|
||||
|
||||
"""
|
||||
self.flavor_dict = flavor_dict
|
||||
template_filename = "%s.config.template" % service_type
|
||||
template_filename = "%s/config.template" % datastore_type
|
||||
self.template = ENV.get_template(template_filename)
|
||||
self.instance_id = instance_id
|
||||
|
||||
@@ -59,60 +66,12 @@ class SingleInstanceConfigTemplate(object):
|
||||
return abs(hash(self.instance_id) % (2 ** 31))
|
||||
|
||||
|
||||
class HeatTemplate(object):
|
||||
template_contents = """HeatTemplateFormatVersion: '2012-12-12'
|
||||
Description: Instance creation
|
||||
Parameters:
|
||||
KeyName: {Type: String}
|
||||
Flavor: {Type: String}
|
||||
VolumeSize: {Type: Number}
|
||||
ServiceType: {Type: String}
|
||||
InstanceId: {Type: String}
|
||||
AvailabilityZone : {Type: String}
|
||||
Resources:
|
||||
BaseInstance:
|
||||
Type: AWS::EC2::Instance
|
||||
Metadata:
|
||||
AWS::CloudFormation::Init:
|
||||
config:
|
||||
files:
|
||||
/etc/guest_info:
|
||||
content:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- ["[DEFAULT]\\nguest_id=", {Ref: InstanceId},
|
||||
"\\nservice_type=", {Ref: ServiceType}]
|
||||
mode: '000644'
|
||||
owner: root
|
||||
group: root
|
||||
Properties:
|
||||
ImageId:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- ["ubuntu_", {Ref: ServiceType}]
|
||||
InstanceType: {Ref: Flavor}
|
||||
KeyName: {Ref: KeyName}
|
||||
AvailabilityZone: {Ref: AvailabilityZone}
|
||||
UserData:
|
||||
Fn::Base64:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- ["#!/bin/bash -v\\n",
|
||||
"/opt/aws/bin/cfn-init\\n",
|
||||
"sudo service trove-guest start\\n"]
|
||||
DataVolume:
|
||||
Type: AWS::EC2::Volume
|
||||
Properties:
|
||||
Size: {Ref: VolumeSize}
|
||||
AvailabilityZone: {Ref: AvailabilityZone}
|
||||
Tags:
|
||||
- {Key: Usage, Value: Test}
|
||||
MountPoint:
|
||||
Type: AWS::EC2::VolumeAttachment
|
||||
Properties:
|
||||
InstanceId: {Ref: BaseInstance}
|
||||
VolumeId: {Ref: DataVolume}
|
||||
Device: /dev/vdb"""
|
||||
|
||||
def template(self):
|
||||
return self.template_contents
|
||||
def load_heat_template(datastore_type):
|
||||
template_filename = "%s/heat.template" % datastore_type
|
||||
try:
|
||||
template_obj = ENV.get_template(template_filename)
|
||||
return template_obj
|
||||
except jinja2.TemplateNotFound:
|
||||
msg = "Missing heat template for %s" % datastore_type
|
||||
LOG.error(msg)
|
||||
raise exception.TroveError(msg)
|
||||
|
||||
@@ -145,7 +145,13 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
availability_zone, root_password):
|
||||
|
||||
security_groups = None
|
||||
if CONF.trove_security_groups_support:
|
||||
|
||||
# If security group support is enabled and heat based instance
|
||||
# orchestration is disabled, create a security group.
|
||||
#
|
||||
# Heat based orchestration handles security group(resource)
|
||||
# in the template defination.
|
||||
if CONF.trove_security_groups_support and not use_heat:
|
||||
try:
|
||||
security_groups = self._create_secgroup()
|
||||
except Exception as e:
|
||||
@@ -322,17 +328,24 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
|
||||
client = create_heat_client(self.context)
|
||||
novaclient = create_nova_client(self.context)
|
||||
cinderclient = create_cinder_client(self.context)
|
||||
heat_template = template.HeatTemplate().template()
|
||||
parameters = {"KeyName": "heatkey",
|
||||
"Flavor": flavor["name"],
|
||||
|
||||
template_obj = template.load_heat_template(service_type)
|
||||
heat_template_unicode = template_obj.render()
|
||||
try:
|
||||
heat_template = heat_template_unicode.encode('ascii')
|
||||
except UnicodeEncodeError:
|
||||
LOG.error(_("heat template ascii encode issue"))
|
||||
raise TroveError("heat template ascii encode issue")
|
||||
|
||||
parameters = {"Flavor": flavor["name"],
|
||||
"VolumeSize": volume_size,
|
||||
"ServiceType": service_type,
|
||||
"InstanceId": self.id,
|
||||
"ImageId": image_id,
|
||||
"AvailabilityZone": availability_zone}
|
||||
stack_name = 'trove-%s' % self.id
|
||||
stack = client.stacks.create(stack_name=stack_name,
|
||||
template=heat_template,
|
||||
parameters=parameters)
|
||||
client.stacks.create(stack_name=stack_name,
|
||||
template=heat_template,
|
||||
parameters=parameters)
|
||||
stack = client.stacks.get(stack_name)
|
||||
|
||||
utils.poll_until(
|
||||
|
||||
72
trove/templates/mysql/heat.template
Normal file
72
trove/templates/mysql/heat.template
Normal file
@@ -0,0 +1,72 @@
|
||||
HeatTemplateFormatVersion: '2012-12-12'
|
||||
Description: Instance creation template for mysql
|
||||
Parameters:
|
||||
Flavor:
|
||||
Type: String
|
||||
VolumeSize:
|
||||
Type: Number
|
||||
Default : '1'
|
||||
InstanceId:
|
||||
Type: String
|
||||
ImageId:
|
||||
Type: String
|
||||
AvailabilityZone:
|
||||
Type: String
|
||||
Default: nova
|
||||
Resources:
|
||||
BaseInstance:
|
||||
Type: AWS::EC2::Instance
|
||||
Metadata:
|
||||
AWS::CloudFormation::Init:
|
||||
config:
|
||||
files:
|
||||
/etc/guest_info:
|
||||
content:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- ["[DEFAULT]\nguest_id=", {Ref: InstanceId},
|
||||
"\nservice_type=mysql"]
|
||||
mode: '000644'
|
||||
owner: root
|
||||
group: root
|
||||
Properties:
|
||||
ImageId: {Ref: ImageId}
|
||||
InstanceType: {Ref: Flavor}
|
||||
AvailabilityZone: {Ref: AvailabilityZone}
|
||||
SecurityGroups : [{Ref: MySqlDbaasSG}]
|
||||
UserData:
|
||||
Fn::Base64:
|
||||
Fn::Join:
|
||||
- ''
|
||||
- ["#!/bin/bash -v\n",
|
||||
"/opt/aws/bin/cfn-init\n",
|
||||
"sudo service trove-guest start\n"]
|
||||
DataVolume:
|
||||
Type: AWS::EC2::Volume
|
||||
Properties:
|
||||
Size: {Ref: VolumeSize}
|
||||
AvailabilityZone: {Ref: AvailabilityZone}
|
||||
Tags:
|
||||
- {Key: Usage, Value: Test}
|
||||
MountPoint:
|
||||
Type: AWS::EC2::VolumeAttachment
|
||||
Properties:
|
||||
InstanceId: {Ref: BaseInstance}
|
||||
VolumeId: {Ref: DataVolume}
|
||||
Device: /dev/vdb
|
||||
MySqlDbaasSG:
|
||||
Type: AWS::EC2::SecurityGroup
|
||||
Properties:
|
||||
GroupDescription: Default Security group for MySQL
|
||||
SecurityGroupIngress:
|
||||
- IpProtocol: "tcp"
|
||||
FromPort: "3306"
|
||||
ToPort: "3306"
|
||||
CidrIp: "0.0.0.0/0"
|
||||
DatabaseIPAddress:
|
||||
Type: AWS::EC2::EIP
|
||||
DatabaseIPAssoc :
|
||||
Type: AWS::EC2::EIPAssociation
|
||||
Properties:
|
||||
InstanceId: {Ref: BaseInstance}
|
||||
EIP: {Ref: DatabaseIPAddress}
|
||||
@@ -12,9 +12,11 @@
|
||||
|
||||
|
||||
import testtools
|
||||
import mock
|
||||
import re
|
||||
|
||||
from trove.common import template
|
||||
from trove.common import exception
|
||||
from trove.tests.unittests.util import util
|
||||
|
||||
|
||||
@@ -23,7 +25,7 @@ class TemplateTest(testtools.TestCase):
|
||||
super(TemplateTest, self).setUp()
|
||||
util.init_db()
|
||||
self.env = template.ENV
|
||||
self.template = self.env.get_template("mysql.config.template")
|
||||
self.template = self.env.get_template("mysql/config.template")
|
||||
self.flavor_dict = {'ram': 1024}
|
||||
self.server_id = "180b5ed1-3e57-4459-b7a3-2aeee4ac012a"
|
||||
|
||||
@@ -60,3 +62,28 @@ class TemplateTest(testtools.TestCase):
|
||||
self.server_id)
|
||||
self.validate_template(config.render(), "query_cache_size",
|
||||
self.flavor_dict, self.server_id)
|
||||
|
||||
|
||||
class HeatTemplateLoadTest(testtools.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HeatTemplateLoadTest, self).setUp()
|
||||
self.fException = mock.Mock(side_effect=
|
||||
lambda *args, **kwargs:
|
||||
_raise(template.jinja2.
|
||||
TemplateNotFound("Test")))
|
||||
|
||||
def _raise(ex):
|
||||
raise ex
|
||||
|
||||
def tearDown(self):
|
||||
super(HeatTemplateLoadTest, self).tearDown()
|
||||
|
||||
def test_heat_template_load_fail(self):
|
||||
self.assertRaises(exception.TroveError,
|
||||
template.load_heat_template,
|
||||
'mysql-blah')
|
||||
|
||||
def test_heat_template_load_success(self):
|
||||
htmpl = template.load_heat_template('mysql')
|
||||
self.assertNotEqual(None, htmpl)
|
||||
|
||||
Reference in New Issue
Block a user