summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2013-09-05 15:27:50 +0000
committerGerrit Code Review <review@openstack.org>2013-09-05 15:27:50 +0000
commit7ad7080a822ade173ea37f2069a997f5802ee86d (patch)
tree8620bfec1d336ef8c98674a47675fe512d99949c
parent6ecab67a1792e49efdcf54b9005565edd15a88fd (diff)
parent98d3de26722c195b33f91168cf67ec35673069a4 (diff)
downloadtrove-7ad7080a822ade173ea37f2069a997f5802ee86d.tar.gz
Merge "Implementing heat as an optional provisioning system"
-rw-r--r--requirements.txt1
-rw-r--r--trove/common/cfg.py5
-rw-r--r--trove/common/remote.py13
-rw-r--r--trove/common/template.py57
-rw-r--r--trove/taskmanager/models.py65
5 files changed, 139 insertions, 2 deletions
diff --git a/requirements.txt b/requirements.txt
index 81b34f50..7c1faa69 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -12,6 +12,7 @@ sqlalchemy-migrate>=0.7.2
netaddr
httplib2
lxml
+python-heatclient>=0.2.3
python-novaclient
python-cinderclient>=1.0.4
python-keystoneclient
diff --git a/trove/common/cfg.py b/trove/common/cfg.py
index 58992135..b53936d9 100644
--- a/trove/common/cfg.py
+++ b/trove/common/cfg.py
@@ -50,6 +50,7 @@ common_opts = [
help='Remote implementation for using fake integration code'),
cfg.StrOpt('nova_compute_url', default='http://localhost:8774/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('trove_auth_url', default='http://0.0.0.0:5000/v2.0'),
cfg.StrOpt('host', default='0.0.0.0'),
@@ -100,6 +101,7 @@ common_opts = [
help='default driver to use for quota checks'),
cfg.StrOpt('taskmanager_queue', default='taskmanager'),
cfg.BoolOpt('use_nova_server_volume', default=False),
+ cfg.BoolOpt('use_heat', default=False),
cfg.StrOpt('fake_mode_events', default='simulated'),
cfg.StrOpt('device_path', default='/dev/vdb'),
cfg.StrOpt('mount_point', default='/var/lib/mysql'),
@@ -107,6 +109,7 @@ common_opts = [
cfg.StrOpt('block_device_mapping', default='vdb'),
cfg.IntOpt('server_delete_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.StrOpt('service_options', default=['mysql']),
cfg.IntOpt('dns_time_out', default=60 * 2),
@@ -169,6 +172,8 @@ common_opts = [
default='trove.common.remote.nova_client'),
cfg.StrOpt('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',
default='trove.common.remote.swift_client'),
cfg.StrOpt('exists_notification_transformer',
diff --git a/trove/common/remote.py b/trove/common/remote.py
index 3fdf2a9f..f6b91d75 100644
--- a/trove/common/remote.py
+++ b/trove/common/remote.py
@@ -18,6 +18,7 @@
from trove.common import cfg
from trove.openstack.common.importutils import import_class
from cinderclient.v2 import client as CinderClient
+from heatclient.v1 import client as HeatClient
from novaclient.v1_1.client import Client
from swiftclient.client import Connection
@@ -28,6 +29,7 @@ PROXY_AUTH_URL = CONF.trove_auth_url
VOLUME_URL = CONF.cinder_url
OBJECT_STORE_URL = CONF.swift_url
USE_SNET = CONF.backup_use_snet
+HEAT_URL = CONF.heat_url
def dns_client(context):
@@ -68,6 +70,16 @@ def cinder_client(context):
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):
client = Connection(preauthurl=OBJECT_STORE_URL + context.tenant,
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_swift_client = import_class(CONF.remote_swift_client)
create_cinder_client = import_class(CONF.remote_cinder_client)
+create_heat_client = import_class(CONF.remote_heat_client)
diff --git a/trove/common/template.py b/trove/common/template.py
index f1ed754d..8edb355b 100644
--- a/trove/common/template.py
+++ b/trove/common/template.py
@@ -45,3 +45,60 @@ class SingleInstanceConfigTemplate(object):
self.config_contents = self.template.render(
flavor=self.flavor_dict)
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
diff --git a/trove/taskmanager/models.py b/trove/taskmanager/models.py
index c786badd..6d2fee93 100644
--- a/trove/taskmanager/models.py
+++ b/trove/taskmanager/models.py
@@ -17,6 +17,9 @@ import os.path
from cinderclient import exceptions as cinder_exceptions
from eventlet import greenthread
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 template
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 TroveError
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 swiftclient.client import ClientException
from trove.common.utils import poll_until
from trove.instance import models as inst_models
from trove.instance.models import BuiltInstance
from trove.instance.models import FreshInstance
+
from trove.instance.models import InstanceStatus
from trove.instance.models import InstanceServiceStatus
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.
RESIZE_TIME_OUT = CONF.resize_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_TIMEOUT = CONF.usage_timeout # seconds.
use_nova_server_volume = CONF.use_nova_server_volume
+use_heat = CONF.use_heat
class NotifyMixin(object):
@@ -128,7 +136,14 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
def create_instance(self, flavor, image_id, databases, users,
service_type, volume_size, security_groups,
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(
flavor['id'],
image_id,
@@ -142,6 +157,7 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
security_groups,
service_type,
volume_size)
+
try:
self._create_dns_entry()
except Exception as e:
@@ -237,6 +253,42 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
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,
security_groups, service_type,
volume_size):
@@ -305,6 +357,9 @@ class FreshInstanceTasks(FreshInstance, NotifyMixin, ConfigurationMixin):
v_ref = volume_client.volumes.get(volume_ref.id)
if v_ref.status in ['error']:
raise VolumeCreationFailure()
+ return self._build_volume(v_ref)
+
+ def _build_volume(self, v_ref):
LOG.debug(_("Created volume %s") % v_ref)
# The mapping is in the format:
# <id>:[<type>]:[<size(GB)>]:[<delete_on_terminate>]
@@ -417,7 +472,13 @@ class BuiltInstanceTasks(BuiltInstance, NotifyMixin, ConfigurationMixin):
server_id = self.db_info.compute_instance_id
old_server = self.nova_client.servers.get(server_id)
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:
LOG.error("Error during delete compute server %s "
% self.server.id)