monitor, respawn: keep vnf id when respawning new vnf

Change-Id: I5ac85e90dd1398a1670c5ec16edf85182c2f3cc7
This commit is contained in:
Isaku Yamahata 2015-05-12 02:49:59 -07:00
parent 41c6e00729
commit 85273fb573
5 changed files with 132 additions and 16 deletions

View File

@ -57,7 +57,7 @@ def upgrade(active_plugins=None, options=None):
)
op.create_table(
'devices',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('id', sa.String(length=255), nullable=False),
sa.Column('tenant_id', sa.String(length=255), nullable=True),
sa.Column('name', sa.String(length=255), nullable=True),
sa.Column('template_id', sa.String(length=36), nullable=True),
@ -70,9 +70,9 @@ def upgrade(active_plugins=None, options=None):
op.create_table(
'deviceattributes',
sa.Column('id', sa.String(length=36), nullable=False),
sa.Column('device_id', sa.String(length=36)),
sa.Column('device_id', sa.String(length=255)),
sa.Column('key', sa.String(length=255), nullable=False),
sa.Column('value', sa.String(length=255), nullable=True),
sa.Column('value', sa.String(length=4096), nullable=True),
sa.ForeignKeyConstraint(['device_id'], ['devices.id'], ),
sa.PrimaryKeyConstraint('id'),
)

View File

@ -35,6 +35,7 @@ from tacker.db import models_v1
from tacker.extensions import servicevm
from tacker import manager
from tacker.openstack.common import log as logging
from tacker.openstack.common import uuidutils
from tacker.plugins.common import constants
LOG = logging.getLogger(__name__)
@ -91,11 +92,15 @@ class DeviceTemplateAttribute(model_base.BASE, models_v1.HasId):
value = sa.Column(sa.String(4096), nullable=True)
class Device(model_base.BASE, models_v1.HasId, models_v1.HasTenant):
class Device(model_base.BASE, models_v1.HasTenant):
"""Represents devices that hosts services.
Here the term, 'VM', is intentionally avoided because it can be
VM or other container.
"""
id = sa.Column(sa.String(255),
primary_key=True,
default=uuidutils.generate_uuid)
template_id = sa.Column(sa.String(36), sa.ForeignKey('devicetemplates.id'))
template = orm.relationship('DeviceTemplate')
@ -123,7 +128,7 @@ class DeviceAttribute(model_base.BASE, models_v1.HasId):
like nova, heat or others. e.g. image-id, flavor-id for Nova.
The interpretation is up to actual driver of hosting device.
"""
device_id = sa.Column(sa.String(36), sa.ForeignKey('devices.id'),
device_id = sa.Column(sa.String(255), sa.ForeignKey('devices.id'),
nullable=False)
key = sa.Column(sa.String(255), nullable=False)
# json encoded value. example
@ -694,6 +699,37 @@ class ServiceResourcePluginDb(servicevm.ServiceVMPluginBase,
device_db.update({'status': constants.DEAD})
return True
# used by failure policy
def update_dead_device(self, device_id, dead_device_dict):
# ugly hack...
dead_device_id = dead_device_dict['id']
context = t_context.get_admin_context()
with context.session.begin(subtransactions=True):
dead_device_db = Device(
id=dead_device_id,
tenant_id=dead_device_dict['tenant_id'],
name=dead_device_dict['name'],
instance_id=dead_device_dict['instance_id'],
template_id=dead_device_dict['template_id'],
mgmt_url=dead_device_dict['mgmt_url'],
status=constants.DEAD)
context.session.add(dead_device_db)
(self._model_query(context, DeviceAttribute).
filter(DeviceAttribute.device_id == device_id).
update({'device_id': dead_device_id}))
for (key, value) in dead_device_dict['attributes'].items():
self._device_attribute_update_or_create(
context, dead_device_id, key, value)
try:
(self._model_query(context, Device).
filter(Device.id == device_id).
filter(Device.status == constants.DEAD).
delete())
except orm_exc.NoResultFound:
LOG.exception(_('no device found %s'), device_id)
###########################################################################
# logical service instance

View File

@ -26,6 +26,7 @@ import yaml
from heatclient import client as heat_client
from heatclient import exc as heatException
from keystoneclient.v2_0 import client as ks_client
from oslo_config import cfg
from tacker.common import log
@ -199,11 +200,16 @@ class DeviceHeat(abstract_driver.DeviceAbstractDriver):
properties['config_drive'] = True
properties.setdefault('metadata', {}).update(config)
fields['template'] = yaml.dump(template_dict)
heat_template_yaml = yaml.dump(template_dict)
fields['template'] = heat_template_yaml
if not device['attributes'].get('heat_template'):
device['attributes']['heat_template'] = heat_template_yaml
if 'stack_name' not in fields:
name = (__name__ + '_' + self.__class__.__name__ + '-' +
device['id'])
if device['attributes'].get('failure_count'):
name += ('-%s') % str(device['attributes']['failure_count'])
fields['stack_name'] = name
# service context is ignored
@ -307,12 +313,23 @@ class DeviceHeat(abstract_driver.DeviceAbstractDriver):
class HeatClient:
def __init__(self, context, password=None):
# context, password are unused
auth_url = CONF.keystone_authtoken.auth_uri + '/v2.0'
authtoken = CONF.keystone_authtoken
kc = ks_client.Client(
tenant_name=authtoken.project_name,
username=authtoken.username,
password=authtoken.password,
auth_url=auth_url)
token = kc.service_catalog.get_token()
api_version = "1"
endpoint = "%s/%s" % (cfg.CONF.servicevm_heat.heat_uri, context.tenant)
endpoint = "%s/%s" % (cfg.CONF.servicevm_heat.heat_uri,
token['tenant_id'])
kwargs = {
'token': context.auth_token,
'username': context.user_name,
'password': password
'token': token['id'],
'tenant_name': authtoken.project_name,
'username': authtoken.username,
}
self.client = heat_client.Client(api_version, endpoint, **kwargs)
self.stacks = self.client.stacks

View File

@ -31,6 +31,7 @@ from tacker.agent.linux import utils as linux_utils
from tacker import context as t_context
from tacker.i18n import _LW
from tacker.openstack.common import log as logging
from tacker.vm.drivers.heat import heat
LOG = logging.getLogger(__name__)
@ -108,6 +109,7 @@ class DeviceStatus(object):
'management_ip_address': device_dict['mgmt_url'],
'boot_wait': cfg.CONF.monitor.boot_wait,
'down_cb': down_cb,
'device': device_dict,
}
def add_hosting_device(self, new_device):
@ -163,15 +165,26 @@ class FailurePolicy(object):
_POLICIES = {}
@staticmethod
def register(policy):
def register(policy, infra_driver=None):
def _register(cls):
cls._POLICIES[policy] = cls
cls._POLICIES.setdefault(policy, {})[infra_driver] = cls
return cls
return _register
@classmethod
def get_policy(cls, policy):
return cls._POLICIES.get(policy)
def get_policy(cls, policy, device):
failure_clses = cls._POLICIES.get(policy)
if not failure_clses:
return None
infra_driver = device['device_template'].get('infra_driver')
cls = failure_clses.get(infra_driver)
if cls:
return cls
return failure_clses.get(None)
@abc.abstractmethod
def on_failure(cls, plugin, device_dict):
pass
@FailurePolicy.register('respawn')
@ -206,6 +219,54 @@ class Respawn(FailurePolicy):
LOG.info(_('respawned new device %s'), new_device_dict['id'])
@FailurePolicy.register('respawn', 'heat')
class RespawnHeat(FailurePolicy):
@classmethod
def on_failure(cls, plugin, device_dict):
device_id = device_dict['id']
LOG.error(_('device %s dead'), device_id)
attributes = device_dict['attributes']
failure_count = int(attributes.get('failure_count', '0')) + 1
failure_count_str = str(failure_count)
attributes['failure_count'] = failure_count_str
attributes['dead_instance_id_' + failure_count_str] = device_dict[
'instance_id']
attributes = device_dict['attributes'].copy()
attributes['dead_device_id'] = device_dict['id']
new_device = {'attributes': attributes}
for key in ('id', 'tenant_id', 'template_id', 'name'):
new_device[key] = device_dict[key]
LOG.debug(_('new_device %s'), new_device)
# update device_dict and kill dead heat stack
# ungly hack to keep id unchanged
heatclient = heat.HeatClient(None)
heatclient.delete(device_dict['instance_id'])
device_dict['id'] = device_dict['id'] + '-DEAD-' + failure_count_str
plugin.update_dead_device(device_id, device_dict)
# keystone v2.0 specific
auth_url = CONF.keystone_authtoken.auth_uri + '/v2.0'
authtoken = CONF.keystone_authtoken
kc = ks_client.Client(
tenant_name=authtoken.project_name,
username=authtoken.username,
password=authtoken.password,
auth_url=auth_url)
token = kc.service_catalog.get_token()
context = t_context.get_admin_context()
context.tenant_name = authtoken.project_name
context.user_name = authtoken.username
context.auth_token = token['id']
context.tenant_id = token['tenant_id']
context.user_id = token['user_id']
new_device_dict = plugin.create_device(context, {'device': new_device})
LOG.info(_('respawned new device %s'), new_device_dict['id'])
@FailurePolicy.register('log_and_kill')
class LogAndKill(FailurePolicy):
@classmethod

View File

@ -19,6 +19,7 @@
# under the License.
#
# @author: Isaku Yamahata, Intel Corporation.
import copy
import eventlet
import inspect
@ -255,13 +256,14 @@ class ServiceVMPlugin(vm_db.ServiceResourcePluginDb, ServiceVMMgmtMixin):
self._create_device_status(context, device_id, new_status)
dev_attrs = device_dict['attributes']
if dev_attrs.get('monitoring_policy') == 'ping':
device_dict_copy = device_dict.copy()
device_dict_copy = copy.deepcopy(device_dict)
def down_cb(hosting_device_):
if self._mark_device_dead(device_id):
self._device_status.mark_dead(device_id)
failure_cls = monitor.FailurePolicy.get_policy(
dev_attrs.get('failure_policy'))
device_dict_copy['attributes'].get('failure_policy'),
device_dict_copy)
if failure_cls:
failure_cls.on_failure(self, device_dict_copy)