heat infra driver

Change-Id: I7f2e985a64d2e7e8cc0ca265815df3ed498742f7
This commit is contained in:
Isaku Yamahata 2015-04-16 17:56:18 -07:00
parent ff26d55562
commit 052ac3e0b6
6 changed files with 234 additions and 3 deletions

View File

@ -491,7 +491,8 @@ service_provider=LOADBALANCER:Haproxy:tacker.services.loadbalancer.drivers.hapro
# Specify drivers for hosting device
# exmpale: infra_driver = noop
# exmpale: infra_driver = nova
infra_driver = nova
# exmpale: infra_driver = heat
infra_driver = heat
# Specify drivers for mgmt
# exmpale: mgmt_driver = noop
@ -508,6 +509,10 @@ username = nova
auth_url = http://127.0.0.1:35357
auth_plugin = password
[servicevm_heat]
heat_uri = http://localhost:8004/v1
stack_retries = 5
stack_retry_wait = 3
[servicevm_agent]
# VM agent requires that an interface driver be set. Choose the one that best

View File

@ -51,6 +51,7 @@ tacker.openstack.common.cache.backends =
tacker.servicevm.device.drivers =
noop = tacker.tests.unit.services.vm.drivers.noop:DeviceNoop
nova = tacker.vm.drivers.nova.nova:DeviceNova
heat = tacker.vm.drivers.heat.heat:DeviceHeat
tacker.servicevm.mgmt.drivers =
noop = tacker.tests.unit.services.vm.mgmt_drivers.noop:DeviceMgmtNoop
agent_rpc = tacker.vm.mgmt_drivers.rpc.rpc:AgentRpcMGMTDriver

View File

@ -30,6 +30,12 @@ ATTR_KEY_IMAGE = 'image'
ATTR_KEY_FLAVOR = 'flavor'
ATTR_KEY_MGMT_NETWORK = 'mgmt-network'
# attribute key for device template for heat
ATTR_KEY_HEAT_STACK_NAME = 'stack_name'
ATTR_KEY_HEAT_TEMPLATE_URL = 'template_url'
ATTR_KEY_HEAT_TEMPLATE = 'template'
ATTR_KEY_HEAT_FILES = 'files'
ATTR_KEY_HEAT_PARAMETERS = 'parameters'
# Role of service context
ROLE_NONE = 'None'

View File

View File

@ -0,0 +1,219 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2015 Intel Corporation.
# Copyright 2015 Isaku Yamahata <isaku.yamahata at intel com>
# <isaku.yamahata at gmail com>
# All Rights Reserved.
#
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# @author: Isaku Yamahata, Intel Corporation.
# shamelessly many codes are stolen from gbp simplechain_driver.py
import time
from heatclient import client as heat_client
from heatclient import exc as heatException
from oslo_config import cfg
from tacker.common import log
from tacker.openstack.common import jsonutils
from tacker.openstack.common import log as logging
from tacker.vm.drivers import abstract_driver
LOG = logging.getLogger(__name__)
CONF = cfg.CONF
OPTS = [
cfg.StrOpt('heat_uri',
default='http://localhost:8004/v1',
help=_("Heat server address to create services "
"specified in the service chain.")),
cfg.IntOpt('stack_retries',
default=10,
help=_("Number of attempts to retry for stack deletion")),
cfg.IntOpt('stack_retry_wait',
default=5,
help=_("Wait time between two successive stack delete "
"retries")),
]
CONF.register_opts(OPTS, group='servicevm_heat')
STACK_RETRIES = cfg.CONF.servicevm_heat.stack_retries
STACK_RETRY_WAIT = cfg.CONF.servicevm_heat.stack_retry_wait
class DeviceHeat(abstract_driver.DeviceAbstractDriver):
"""Heat driver of hosting device."""
def __init__(self):
super(DeviceHeat, self).__init__()
def get_type(self):
return 'heat'
def get_name(self):
return 'heat'
def get_description(self):
return 'Heat infra driver'
@log.log
def create(self, plugin, context, device):
LOG.debug(_('device %s'), device)
heatclient_ = HeatClient(context)
attributes = device['device_template']['attributes'].copy()
fields = dict((key, attributes.pop(key)) for key
in ('stack_name', 'template_url', 'template')
if key in attributes)
for key in ('files', 'parameters'):
if key in attributes:
fields[key] = jsonutils.loads(attributes.pop(key))
fields.setdefault('parameters', {}).update(attributes)
# overwrite parameters with given kwargs for device creation
kwargs = device['kwargs'].copy()
fields.update(dict((key, kwargs.pop(key)) for key
in ('stack_name', 'template_url', 'template')
if key in kwargs))
for key in ('files', 'parameters'):
if key in kwargs:
kwargs[key] = jsonutils.loads(kwargs.pop(key))
fields['parameters'].update(kwargs)
if 'stack_name' not in fields:
name = (__name__ + '_' + self.__class__.__name__ + '-' +
device['id'])
fields['stack_name'] = name
# service context is ignored
LOG.debug(_('service_context: %s'), device.get('service_context', []))
LOG.debug(_('fields: %s'), fields)
stack = heatclient_.create(fields)
return stack['stack']['id']
def create_wait(self, plugin, context, device_id):
heatclient_ = HeatClient(context)
stack = heatclient_.get(device_id)
status = stack.stack_status
stack_retries = STACK_RETRIES
while status == 'CREATE_IN_PROGRESS' and stack_retries > 0:
time.sleep(STACK_RETRY_WAIT)
try:
stack = heatclient_.get(device_id)
except Exception:
LOG.exception(_("Device Instance cleanup may not have "
"happened because Heat API request failed "
"while waiting for the stack %(stack)s to be "
"deleted"), {'stack': device_id})
break
status = stack.stack_status
LOG.debug(_('status: %s'), status)
stack_retries = stack_retries - 1
LOG.debug(_('status: %s'), status)
if stack_retries == 0:
LOG.warn(_("Resource creation is"
" not completed within %(wait)s seconds as "
"creation of Stack %(stack)s is not completed"),
{'wait': (STACK_RETRIES * STACK_RETRY_WAIT),
'stack': device_id})
if status != 'CREATE_COMPLETE':
raise RuntimeError(_("creation of server %s faild") % device_id)
def update(self, plugin, context, device):
# do nothing but checking if the stack exists at the moment
heatclient_ = HeatClient(context)
heatclient_.get(device['id'])
def update_wait(self, plugin, context, device_id):
# do nothing but checking if the stack exists at the moment
heatclient_ = HeatClient(context)
heatclient_.get(device_id)
def delete(self, plugin, context, device_id):
heatclient_ = HeatClient(context)
heatclient_.delete(device_id)
@log.log
def delete_wait(self, plugin, context, device_id):
heatclient_ = HeatClient(context)
stack = heatclient_.get(device_id)
status = stack.satck_status
stack_retries = STACK_RETRIES
while (status == 'DELETE_IN_PROGRESS' and stack_retries > 0):
time.sleep(STACK_RETRY_WAIT)
try:
stack = heatclient_.get(device_id)
except Exception:
LOG.exception(_("Device Instance cleanup may not have "
"happened because Heat API request failed "
"while waiting for the stack %(stack)s to be "
"deleted"), {'stack': device_id})
break
status = stack.satck_status
stack_retries = stack_retries - 1
if stack_retries == 0:
LOG.warn(_("Resource cleanup for device is"
" not completed within %(wait)s seconds as "
"deletion of Stack %(stack)s is not completed"),
{'wait': (STACK_RETRIES * STACK_RETRY_WAIT),
'stack': device_id})
if status != 'DELETE_COMPLETE':
LOG.warn(_("device (%(device_id)d) deletion is not completed. "
"%(stack_status)s"),
{'device_id': device_id, 'stack_status': status})
@log.log
def attach_interface(self, plugin, context, device_id, port_id):
raise NotImplementedError()
@log.log
def dettach_interface(self, plugin, context, device_id, port_id):
raise NotImplementedError()
class HeatClient:
def __init__(self, context, password=None):
api_version = "1"
endpoint = "%s/%s" % (cfg.CONF.servicevm_heat.heat_uri, context.tenant)
kwargs = {
'token': context.auth_token,
'username': context.user_name,
'password': password
}
self.client = heat_client.Client(api_version, endpoint, **kwargs)
self.stacks = self.client.stacks
def create(self, fields):
fields = fields.copy()
fields.update({
'timeout_mins': 10,
'disable_rollback': True})
if 'password' in fields.get('template', {}):
fields['password'] = fields['template']['password']
return self.stacks.create(**fields)
def delete(self, stack_id):
try:
self.stacks.delete(stack_id)
except heatException.HTTPNotFound:
LOG.warn(_("Stack %(stack)s created by service chain driver is "
"not found at cleanup"), {'stack': stack_id})
def get(self, stack_id):
return self.stacks.get(stack_id)

View File

@ -156,8 +156,8 @@ class ServiceVMPlugin(vm_db.ServiceResourcePluginDb, ServiceVMMgmtMixin):
"""ServiceVMPlugin which supports ServiceVM framework
"""
OPTS = [
cfg.MultiStrOpt(
'infra_driver', default=[],
cfg.ListOpt(
'infra_driver', default=['heat'],
help=_('Hosting device drivers servicevm plugin will use')),
]
cfg.CONF.register_opts(OPTS, 'servicevm')