Merge "Implementing heat as an optional provisioning system"

This commit is contained in:
Jenkins
2013-09-05 15:27:50 +00:00
committed by Gerrit Code Review
5 changed files with 139 additions and 2 deletions

View File

@@ -12,6 +12,7 @@ sqlalchemy-migrate>=0.7.2
netaddr netaddr
httplib2 httplib2
lxml lxml
python-heatclient>=0.2.3
python-novaclient python-novaclient
python-cinderclient>=1.0.4 python-cinderclient>=1.0.4
python-keystoneclient python-keystoneclient

View File

@@ -50,6 +50,7 @@ common_opts = [
help='Remote implementation for using fake integration code'), help='Remote implementation for using fake integration code'),
cfg.StrOpt('nova_compute_url', default='http://localhost:8774/v2'), cfg.StrOpt('nova_compute_url', default='http://localhost:8774/v2'),
cfg.StrOpt('cinder_url', default='http://localhost:8776/v2'), cfg.StrOpt('cinder_url', default='http://localhost:8776/v2'),
cfg.StrOpt('heat_url', default='http://localhost:8004/v1'),
cfg.StrOpt('swift_url', default='http://localhost:8080/v1/AUTH_'), cfg.StrOpt('swift_url', default='http://localhost:8080/v1/AUTH_'),
cfg.StrOpt('trove_auth_url', default='http://0.0.0.0:5000/v2.0'), cfg.StrOpt('trove_auth_url', default='http://0.0.0.0:5000/v2.0'),
cfg.StrOpt('host', default='0.0.0.0'), cfg.StrOpt('host', default='0.0.0.0'),
@@ -100,6 +101,7 @@ common_opts = [
help='default driver to use for quota checks'), help='default driver to use for quota checks'),
cfg.StrOpt('taskmanager_queue', default='taskmanager'), cfg.StrOpt('taskmanager_queue', default='taskmanager'),
cfg.BoolOpt('use_nova_server_volume', default=False), cfg.BoolOpt('use_nova_server_volume', default=False),
cfg.BoolOpt('use_heat', default=False),
cfg.StrOpt('fake_mode_events', default='simulated'), cfg.StrOpt('fake_mode_events', default='simulated'),
cfg.StrOpt('device_path', default='/dev/vdb'), cfg.StrOpt('device_path', default='/dev/vdb'),
cfg.StrOpt('mount_point', default='/var/lib/mysql'), cfg.StrOpt('mount_point', default='/var/lib/mysql'),
@@ -107,6 +109,7 @@ common_opts = [
cfg.StrOpt('block_device_mapping', default='vdb'), cfg.StrOpt('block_device_mapping', default='vdb'),
cfg.IntOpt('server_delete_time_out', default=2), cfg.IntOpt('server_delete_time_out', default=2),
cfg.IntOpt('volume_time_out', default=2), cfg.IntOpt('volume_time_out', default=2),
cfg.IntOpt('heat_time_out', default=60),
cfg.IntOpt('reboot_time_out', default=60 * 2), cfg.IntOpt('reboot_time_out', default=60 * 2),
cfg.StrOpt('service_options', default=['mysql']), cfg.StrOpt('service_options', default=['mysql']),
cfg.IntOpt('dns_time_out', default=60 * 2), cfg.IntOpt('dns_time_out', default=60 * 2),
@@ -169,6 +172,8 @@ common_opts = [
default='trove.common.remote.nova_client'), default='trove.common.remote.nova_client'),
cfg.StrOpt('remote_cinder_client', cfg.StrOpt('remote_cinder_client',
default='trove.common.remote.cinder_client'), default='trove.common.remote.cinder_client'),
cfg.StrOpt('remote_heat_client',
default='trove.common.remote.heat_client'),
cfg.StrOpt('remote_swift_client', cfg.StrOpt('remote_swift_client',
default='trove.common.remote.swift_client'), default='trove.common.remote.swift_client'),
cfg.StrOpt('exists_notification_transformer', cfg.StrOpt('exists_notification_transformer',

View File

@@ -18,6 +18,7 @@
from trove.common import cfg from trove.common import cfg
from trove.openstack.common.importutils import import_class from trove.openstack.common.importutils import import_class
from cinderclient.v2 import client as CinderClient from cinderclient.v2 import client as CinderClient
from heatclient.v1 import client as HeatClient
from novaclient.v1_1.client import Client from novaclient.v1_1.client import Client
from swiftclient.client import Connection from swiftclient.client import Connection
@@ -28,6 +29,7 @@ PROXY_AUTH_URL = CONF.trove_auth_url
VOLUME_URL = CONF.cinder_url VOLUME_URL = CONF.cinder_url
OBJECT_STORE_URL = CONF.swift_url OBJECT_STORE_URL = CONF.swift_url
USE_SNET = CONF.backup_use_snet USE_SNET = CONF.backup_use_snet
HEAT_URL = CONF.heat_url
def dns_client(context): def dns_client(context):
@@ -68,6 +70,16 @@ def cinder_client(context):
return client return client
def heat_client(context):
endpoint = "%s/%s/" % (HEAT_URL, context.tenant)
client = HeatClient.Client(username=context.user,
password="radmin",
token=context.auth_token,
os_no_client_auth=True,
endpoint=endpoint)
return client
def swift_client(context): def swift_client(context):
client = Connection(preauthurl=OBJECT_STORE_URL + context.tenant, client = Connection(preauthurl=OBJECT_STORE_URL + context.tenant,
preauthtoken=context.auth_token, preauthtoken=context.auth_token,
@@ -81,3 +93,4 @@ create_guest_client = import_class(CONF.remote_guest_client)
create_nova_client = import_class(CONF.remote_nova_client) create_nova_client = import_class(CONF.remote_nova_client)
create_swift_client = import_class(CONF.remote_swift_client) create_swift_client = import_class(CONF.remote_swift_client)
create_cinder_client = import_class(CONF.remote_cinder_client) create_cinder_client = import_class(CONF.remote_cinder_client)
create_heat_client = import_class(CONF.remote_heat_client)

View File

@@ -45,3 +45,60 @@ class SingleInstanceConfigTemplate(object):
self.config_contents = self.template.render( self.config_contents = self.template.render(
flavor=self.flavor_dict) flavor=self.flavor_dict)
return self.config_contents return self.config_contents
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}
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}
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: nova
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

View File

@@ -17,6 +17,9 @@ import os.path
from cinderclient import exceptions as cinder_exceptions from cinderclient import exceptions as cinder_exceptions
from eventlet import greenthread from eventlet import greenthread
from novaclient import exceptions as nova_exceptions from novaclient import exceptions as nova_exceptions
from novaclient import base
from novaclient.v1_1 import servers
from novaclient.v1_1 import volumes
from trove.common import cfg from trove.common import cfg
from trove.common import template from trove.common import template
from trove.common import utils from trove.common import utils
@@ -26,12 +29,15 @@ from trove.common.exception import PollTimeOut
from trove.common.exception import VolumeCreationFailure from trove.common.exception import VolumeCreationFailure
from trove.common.exception import TroveError from trove.common.exception import TroveError
from trove.common.remote import create_dns_client from trove.common.remote import create_dns_client
from trove.common.remote import create_nova_client
from trove.common.remote import create_heat_client
from trove.common.remote import create_cinder_client from trove.common.remote import create_cinder_client
from swiftclient.client import ClientException from swiftclient.client import ClientException
from trove.common.utils import poll_until from trove.common.utils import poll_until
from trove.instance import models as inst_models from trove.instance import models as inst_models
from trove.instance.models import BuiltInstance from trove.instance.models import BuiltInstance
from trove.instance.models import FreshInstance from trove.instance.models import FreshInstance
from trove.instance.models import InstanceStatus from trove.instance.models import InstanceStatus
from trove.instance.models import InstanceServiceStatus from trove.instance.models import InstanceServiceStatus
from trove.instance.models import ServiceStatuses from trove.instance.models import ServiceStatuses
@@ -49,10 +55,12 @@ VOLUME_TIME_OUT = CONF.volume_time_out # seconds.
DNS_TIME_OUT = CONF.dns_time_out # seconds. DNS_TIME_OUT = CONF.dns_time_out # seconds.
RESIZE_TIME_OUT = CONF.resize_time_out # seconds. RESIZE_TIME_OUT = CONF.resize_time_out # seconds.
REVERT_TIME_OUT = CONF.revert_time_out # seconds. REVERT_TIME_OUT = CONF.revert_time_out # seconds.
HEAT_TIME_OUT = CONF.heat_time_out # seconds.
USAGE_SLEEP_TIME = CONF.usage_sleep_time # seconds. USAGE_SLEEP_TIME = CONF.usage_sleep_time # seconds.
USAGE_TIMEOUT = CONF.usage_timeout # seconds. USAGE_TIMEOUT = CONF.usage_timeout # seconds.
use_nova_server_volume = CONF.use_nova_server_volume use_nova_server_volume = CONF.use_nova_server_volume
use_heat = CONF.use_heat
class NotifyMixin(object): class NotifyMixin(object):
@@ -128,7 +136,14 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
def create_instance(self, flavor, image_id, databases, users, def create_instance(self, flavor, image_id, databases, users,
service_type, volume_size, security_groups, service_type, volume_size, security_groups,
backup_id): backup_id):
if use_nova_server_volume: if use_heat:
server, volume_info = self._create_server_volume_heat(
flavor,
image_id,
security_groups,
service_type,
volume_size)
elif use_nova_server_volume:
server, volume_info = self._create_server_volume( server, volume_info = self._create_server_volume(
flavor['id'], flavor['id'],
image_id, image_id,
@@ -142,6 +157,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
security_groups, security_groups,
service_type, service_type,
volume_size) volume_size)
try: try:
self._create_dns_entry() self._create_dns_entry()
except Exception as e: except Exception as e:
@@ -237,6 +253,42 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
return server, volume_info return server, volume_info
def _create_server_volume_heat(self, flavor, image_id,
security_groups, service_type,
volume_size):
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"],
"VolumeSize": volume_size,
"ServiceType": "mysql",
"InstanceId": self.id}
stack_name = 'trove-%s' % self.id
stack = client.stacks.create(stack_name=stack_name,
template=heat_template,
parameters=parameters)
stack = client.stacks.get(stack_name)
utils.poll_until(
lambda: client.stacks.get(stack_name),
lambda stack: stack.stack_status in ['CREATE_COMPLETE',
'CREATE_FAILED'],
sleep_time=2,
time_out=HEAT_TIME_OUT)
resource = client.resources.get(stack.id, 'BaseInstance')
server = novaclient.servers.get(resource.physical_resource_id)
resource = client.resources.get(stack.id, 'DataVolume')
volume = cinderclient.volumes.get(resource.physical_resource_id)
volume_info = self._build_volume(volume)
self.update_db(compute_instance_id=server.id, volume_id=volume.id)
return server, volume_info
def _create_server_volume_individually(self, flavor_id, image_id, def _create_server_volume_individually(self, flavor_id, image_id,
security_groups, service_type, security_groups, service_type,
volume_size): volume_size):
@@ -305,6 +357,9 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
v_ref = volume_client.volumes.get(volume_ref.id) v_ref = volume_client.volumes.get(volume_ref.id)
if v_ref.status in ['error']: if v_ref.status in ['error']:
raise VolumeCreationFailure() raise VolumeCreationFailure()
return self._build_volume(v_ref)
def _build_volume(self, v_ref):
LOG.debug(_("Created volume %s") % v_ref) LOG.debug(_("Created volume %s") % v_ref)
# The mapping is in the format: # The mapping is in the format:
# <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>] # <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>]
@@ -417,7 +472,13 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
server_id = self.db_info.compute_instance_id server_id = self.db_info.compute_instance_id
old_server = self.nova_client.servers.get(server_id) old_server = self.nova_client.servers.get(server_id)
try: try:
self.server.delete() if use_heat:
# Delete the server via heat
heatclient = create_heat_client(self.context)
name = 'trove-%s' % self.id
heatclient.stacks.delete(name)
else:
self.server.delete()
except Exception as ex: except Exception as ex:
LOG.error("Error during delete compute server %s " LOG.error("Error during delete compute server %s "
% self.server.id) % self.server.id)