monitor, respawn: keep vnf id when respawning new vnf
Change-Id: I5ac85e90dd1398a1670c5ec16edf85182c2f3cc7
This commit is contained in:
parent
41c6e00729
commit
85273fb573
|
@ -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'),
|
||||
)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue