Merge "VNF scaling: REST API"
This commit is contained in:
commit
a9139a131c
@ -243,3 +243,17 @@ class InvalidCIDR(BadRequest):
|
|||||||
|
|
||||||
class MgmtDriverException(TackerException):
|
class MgmtDriverException(TackerException):
|
||||||
message = _("VNF configuration failed")
|
message = _("VNF configuration failed")
|
||||||
|
|
||||||
|
|
||||||
|
class VnfPolicyNotFound(NotFound):
|
||||||
|
message = _("Policy %(policy)s does not exist for VNF %(vnf_id)s")
|
||||||
|
|
||||||
|
|
||||||
|
class VnfPolicyActionInvalid(BadRequest):
|
||||||
|
message = _("Invalid action %(action)s for policy %(policy)s, "
|
||||||
|
"should be one of %(valid_acions)s")
|
||||||
|
|
||||||
|
|
||||||
|
class VnfPolicyTypeInvalid(BadRequest):
|
||||||
|
message = _("Invalid type %(type)s for policy %(policy)s, "
|
||||||
|
"should be one of %(valid_types)s")
|
||||||
|
@ -402,6 +402,19 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
|
|||||||
device_db.update({'status': new_status})
|
device_db.update({'status': new_status})
|
||||||
return device_db
|
return device_db
|
||||||
|
|
||||||
|
def _update_vnf_scaling_status(self,
|
||||||
|
context,
|
||||||
|
policy,
|
||||||
|
previous_statuses,
|
||||||
|
status,
|
||||||
|
mgmt_url=None):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
device_db = self._get_device_db(
|
||||||
|
context, policy['vnf']['id'], previous_statuses, status)
|
||||||
|
if mgmt_url:
|
||||||
|
device_db.update({'mgmt_url': mgmt_url})
|
||||||
|
return self._make_device_dict(device_db)
|
||||||
|
|
||||||
def _update_device_pre(self, context, device_id):
|
def _update_device_pre(self, context, device_id):
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
device_db = self._get_device_db(
|
device_db = self._get_device_db(
|
||||||
|
@ -20,8 +20,10 @@ import six
|
|||||||
|
|
||||||
from tacker.api import extensions
|
from tacker.api import extensions
|
||||||
from tacker.api.v1 import attributes as attr
|
from tacker.api.v1 import attributes as attr
|
||||||
|
from tacker.api.v1 import base
|
||||||
from tacker.api.v1 import resource_helper
|
from tacker.api.v1 import resource_helper
|
||||||
from tacker.common import exceptions
|
from tacker.common import exceptions
|
||||||
|
from tacker import manager
|
||||||
from tacker.plugins.common import constants
|
from tacker.plugins.common import constants
|
||||||
from tacker.services import service_base
|
from tacker.services import service_base
|
||||||
|
|
||||||
@ -305,6 +307,41 @@ RESOURCE_ATTRIBUTE_MAP = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
SUB_RESOURCE_ATTRIBUTE_MAP = {
|
||||||
|
'actions': {
|
||||||
|
'parent': {
|
||||||
|
'collection_name': 'vnfs',
|
||||||
|
'member_name': 'vnf'
|
||||||
|
},
|
||||||
|
'members': {
|
||||||
|
'scale': {
|
||||||
|
'parameters': {
|
||||||
|
'policy': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
'validate': {'type:string': None}
|
||||||
|
},
|
||||||
|
'type': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': False,
|
||||||
|
'is_visible': True,
|
||||||
|
'validate': {'type:string': None}
|
||||||
|
},
|
||||||
|
'tenant_id': {
|
||||||
|
'allow_post': True,
|
||||||
|
'allow_put': False,
|
||||||
|
'validate': {'type:string': None},
|
||||||
|
'required_by_policy': False,
|
||||||
|
'is_visible': False
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class Vnfm(extensions.ExtensionDescriptor):
|
class Vnfm(extensions.ExtensionDescriptor):
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_name(cls):
|
def get_name(cls):
|
||||||
@ -333,9 +370,31 @@ class Vnfm(extensions.ExtensionDescriptor):
|
|||||||
special_mappings, RESOURCE_ATTRIBUTE_MAP)
|
special_mappings, RESOURCE_ATTRIBUTE_MAP)
|
||||||
plural_mappings['service_types'] = 'service_type'
|
plural_mappings['service_types'] = 'service_type'
|
||||||
attr.PLURALS.update(plural_mappings)
|
attr.PLURALS.update(plural_mappings)
|
||||||
return resource_helper.build_resource_info(
|
resources = resource_helper.build_resource_info(
|
||||||
plural_mappings, RESOURCE_ATTRIBUTE_MAP, constants.VNFM,
|
plural_mappings, RESOURCE_ATTRIBUTE_MAP, constants.VNFM,
|
||||||
translate_name=True)
|
translate_name=True)
|
||||||
|
plugin = manager.TackerManager.get_service_plugins()[
|
||||||
|
constants.VNFM]
|
||||||
|
for collection_name in SUB_RESOURCE_ATTRIBUTE_MAP:
|
||||||
|
parent = SUB_RESOURCE_ATTRIBUTE_MAP[collection_name]['parent']
|
||||||
|
|
||||||
|
for resource_name in SUB_RESOURCE_ATTRIBUTE_MAP[
|
||||||
|
collection_name]['members']:
|
||||||
|
params = SUB_RESOURCE_ATTRIBUTE_MAP[
|
||||||
|
collection_name]['members'][resource_name]['parameters']
|
||||||
|
|
||||||
|
controller = base.create_resource(collection_name,
|
||||||
|
resource_name,
|
||||||
|
plugin, params,
|
||||||
|
allow_bulk=True,
|
||||||
|
parent=parent)
|
||||||
|
|
||||||
|
resource = extensions.ResourceExtension(
|
||||||
|
collection_name,
|
||||||
|
controller, parent,
|
||||||
|
attr_map=params)
|
||||||
|
resources.append(resource)
|
||||||
|
return resources
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_plugin_interface(cls):
|
def get_plugin_interface(cls):
|
||||||
@ -397,3 +456,8 @@ class VNFMPluginBase(service_base.NFVPluginBase):
|
|||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def delete_vnf(self, context, vnf_id):
|
def delete_vnf(self, context, vnf_id):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@abc.abstractmethod
|
||||||
|
def create_vnf_scale(
|
||||||
|
self, context, vnf_id, scale):
|
||||||
|
pass
|
||||||
|
@ -29,9 +29,13 @@ COMMON_PREFIXES = {
|
|||||||
# Service operation status constants
|
# Service operation status constants
|
||||||
ACTIVE = "ACTIVE"
|
ACTIVE = "ACTIVE"
|
||||||
DOWN = "DOWN"
|
DOWN = "DOWN"
|
||||||
|
|
||||||
PENDING_CREATE = "PENDING_CREATE"
|
PENDING_CREATE = "PENDING_CREATE"
|
||||||
PENDING_UPDATE = "PENDING_UPDATE"
|
PENDING_UPDATE = "PENDING_UPDATE"
|
||||||
PENDING_DELETE = "PENDING_DELETE"
|
PENDING_DELETE = "PENDING_DELETE"
|
||||||
|
PENDING_SCALE_IN = "PENDING_SCALE_IN"
|
||||||
|
PENDING_SCALE_OUT = "PENDING_SCALE_OUT"
|
||||||
|
|
||||||
INACTIVE = "INACTIVE"
|
INACTIVE = "INACTIVE"
|
||||||
DEAD = "DEAD"
|
DEAD = "DEAD"
|
||||||
ERROR = "ERROR"
|
ERROR = "ERROR"
|
||||||
@ -41,3 +45,8 @@ ACTIVE_PENDING_STATUSES = (
|
|||||||
PENDING_CREATE,
|
PENDING_CREATE,
|
||||||
PENDING_UPDATE
|
PENDING_UPDATE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
POLICY_SCALING = 'tosca.policy.tacker.Scaling'
|
||||||
|
POLICY_SCALING_ACTIONS = (ACTION_SCALE_OUT,
|
||||||
|
ACTION_SCALE_IN) = ('out', 'in')
|
||||||
|
POLICY_ACTIONS = {POLICY_SCALING: POLICY_SCALING_ACTIONS}
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
import copy
|
import copy
|
||||||
import inspect
|
import inspect
|
||||||
import six
|
import six
|
||||||
|
import yaml
|
||||||
|
|
||||||
import eventlet
|
import eventlet
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -27,7 +28,7 @@ from oslo_utils import excutils
|
|||||||
from tacker._i18n import _LE
|
from tacker._i18n import _LE
|
||||||
from tacker.api.v1 import attributes
|
from tacker.api.v1 import attributes
|
||||||
from tacker.common import driver_manager
|
from tacker.common import driver_manager
|
||||||
from tacker.common.exceptions import MgmtDriverException
|
from tacker.common import exceptions
|
||||||
from tacker.db.vm import vm_db
|
from tacker.db.vm import vm_db
|
||||||
from tacker.extensions import vnfm
|
from tacker.extensions import vnfm
|
||||||
from tacker.plugins.common import constants
|
from tacker.plugins.common import constants
|
||||||
@ -243,7 +244,7 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
new_status = constants.ACTIVE
|
new_status = constants.ACTIVE
|
||||||
try:
|
try:
|
||||||
self.mgmt_call(context, device_dict, kwargs)
|
self.mgmt_call(context, device_dict, kwargs)
|
||||||
except MgmtDriverException:
|
except exceptions.MgmtDriverException:
|
||||||
LOG.error(_('VNF configuration failed'))
|
LOG.error(_('VNF configuration failed'))
|
||||||
new_status = constants.ERROR
|
new_status = constants.ERROR
|
||||||
self.set_device_error_status_reason(context, device_id,
|
self.set_device_error_status_reason(context, device_id,
|
||||||
@ -319,7 +320,7 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
context=context, device_id=instance_id, auth_attr=vim_auth,
|
context=context, device_id=instance_id, auth_attr=vim_auth,
|
||||||
region_name=region_name)
|
region_name=region_name)
|
||||||
self.mgmt_call(context, device_dict, kwargs)
|
self.mgmt_call(context, device_dict, kwargs)
|
||||||
except MgmtDriverException as e:
|
except exceptions.MgmtDriverException as e:
|
||||||
LOG.error(_('VNF configuration failed'))
|
LOG.error(_('VNF configuration failed'))
|
||||||
new_status = constants.ERROR
|
new_status = constants.ERROR
|
||||||
self.set_device_error_status_reason(context, device_dict['id'],
|
self.set_device_error_status_reason(context, device_dict['id'],
|
||||||
@ -415,6 +416,127 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
|
|
||||||
self.spawn_n(self._delete_device_wait, context, device_dict, vim_auth)
|
self.spawn_n(self._delete_device_wait, context, device_dict, vim_auth)
|
||||||
|
|
||||||
|
def _handle_vnf_scaling(self, context, policy):
|
||||||
|
# validate
|
||||||
|
def _validate_scaling_policy():
|
||||||
|
type = policy['type']
|
||||||
|
|
||||||
|
if type not in constants.POLICY_ACTIONS.keys():
|
||||||
|
raise exceptions.VnfPolicyTypeInvalid(
|
||||||
|
type=type,
|
||||||
|
valid_types=constants.POLICY_ACTIONS.keys(),
|
||||||
|
policy=policy['id']
|
||||||
|
)
|
||||||
|
action = policy['action']
|
||||||
|
|
||||||
|
if action not in constants.POLICY_ACTIONS[type]:
|
||||||
|
raise exceptions.VnfPolicyActionInvalid(
|
||||||
|
action=action,
|
||||||
|
valid_actions=constants.POLICY_ACTIONS[type],
|
||||||
|
policy=policy['id']
|
||||||
|
)
|
||||||
|
|
||||||
|
LOG.debug(_("Policy %s is validated successfully") % policy)
|
||||||
|
|
||||||
|
def _get_status():
|
||||||
|
if policy['action'] == constants.ACTION_SCALE_IN:
|
||||||
|
status = constants.PENDING_SCALE_IN
|
||||||
|
else:
|
||||||
|
status = constants.PENDING_SCALE_OUT
|
||||||
|
|
||||||
|
return status
|
||||||
|
|
||||||
|
# pre
|
||||||
|
def _handle_vnf_scaling_pre():
|
||||||
|
status = _get_status()
|
||||||
|
result = self._update_vnf_scaling_status(context,
|
||||||
|
policy,
|
||||||
|
[constants.ACTIVE],
|
||||||
|
status)
|
||||||
|
LOG.debug(_("Policy %(policy)s vnf is at %(status)s"),
|
||||||
|
{'policy': policy,
|
||||||
|
'status': status})
|
||||||
|
return result
|
||||||
|
|
||||||
|
# post
|
||||||
|
def _handle_vnf_scaling_post(new_status, mgmt_url=None):
|
||||||
|
status = _get_status()
|
||||||
|
result = self._update_vnf_scaling_status(context,
|
||||||
|
policy,
|
||||||
|
[status],
|
||||||
|
new_status,
|
||||||
|
mgmt_url)
|
||||||
|
LOG.debug(_("Policy %(policy)s vnf is at %(status)s"),
|
||||||
|
{'policy': policy,
|
||||||
|
'status': new_status})
|
||||||
|
return result
|
||||||
|
|
||||||
|
# action
|
||||||
|
def _vnf_policy_action():
|
||||||
|
try:
|
||||||
|
self._device_manager.invoke(
|
||||||
|
infra_driver,
|
||||||
|
'scale',
|
||||||
|
plugin=self,
|
||||||
|
context=context,
|
||||||
|
auth_attr=vim_auth,
|
||||||
|
policy=policy,
|
||||||
|
region_name=region_name
|
||||||
|
)
|
||||||
|
LOG.debug(_("Policy %s action is started successfully") %
|
||||||
|
policy)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(_("Policy %s action is failed to start") %
|
||||||
|
policy)
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
vnf['status'] = constants.ERROR
|
||||||
|
self.set_device_error_status_reason(
|
||||||
|
context,
|
||||||
|
policy['vnf_id'],
|
||||||
|
six.text_type(e))
|
||||||
|
_handle_vnf_scaling_post(constants.ERROR)
|
||||||
|
|
||||||
|
# wait
|
||||||
|
def _vnf_policy_action_wait():
|
||||||
|
try:
|
||||||
|
LOG.debug(_("Policy %s action is in progress") %
|
||||||
|
policy)
|
||||||
|
mgmt_url = self._device_manager.invoke(
|
||||||
|
infra_driver,
|
||||||
|
'scale_wait',
|
||||||
|
plugin=self,
|
||||||
|
context=context,
|
||||||
|
auth_attr=vim_auth,
|
||||||
|
policy=policy,
|
||||||
|
region_name=region_name
|
||||||
|
)
|
||||||
|
LOG.debug(_("Policy %s action is completed successfully") %
|
||||||
|
policy)
|
||||||
|
_handle_vnf_scaling_post(constants.ACTIVE, mgmt_url)
|
||||||
|
# TODO(kanagaraj-manickam): Add support for config and mgmt
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(_("Policy %s action is failed to complete") %
|
||||||
|
policy)
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
self.set_device_error_status_reason(
|
||||||
|
context,
|
||||||
|
policy['vnf_id'],
|
||||||
|
six.text_type(e))
|
||||||
|
_handle_vnf_scaling_post(constants.ERROR)
|
||||||
|
|
||||||
|
_validate_scaling_policy()
|
||||||
|
|
||||||
|
vnf = _handle_vnf_scaling_pre()
|
||||||
|
policy['instance_id'] = vnf['instance_id']
|
||||||
|
|
||||||
|
infra_driver = self._infra_driver_name(vnf)
|
||||||
|
vim_auth = self.get_vim(context, vnf)
|
||||||
|
region_name = vnf.get('placement_attr', {}).get('region_name', None)
|
||||||
|
_vnf_policy_action()
|
||||||
|
self.spawn_n(_vnf_policy_action_wait)
|
||||||
|
|
||||||
|
return policy
|
||||||
|
|
||||||
def create_vnf(self, context, vnf):
|
def create_vnf(self, context, vnf):
|
||||||
vnf['device'] = vnf.pop('vnf')
|
vnf['device'] = vnf.pop('vnf')
|
||||||
vnf_attributes = vnf['device']
|
vnf_attributes = vnf['device']
|
||||||
@ -436,3 +558,59 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
|
|||||||
vnfd['device_template'] = vnfd.pop('vnfd')
|
vnfd['device_template'] = vnfd.pop('vnfd')
|
||||||
new_dict = self.create_device_template(context, vnfd)
|
new_dict = self.create_device_template(context, vnfd)
|
||||||
return new_dict
|
return new_dict
|
||||||
|
|
||||||
|
def _make_policy_dict(self, vnf, name, policy):
|
||||||
|
p = {}
|
||||||
|
p['type'] = policy['type']
|
||||||
|
p['properties'] = policy['properties']
|
||||||
|
p['vnf'] = vnf
|
||||||
|
p['name'] = name
|
||||||
|
p['id'] = p['name']
|
||||||
|
return p
|
||||||
|
|
||||||
|
def get_vnf_policies(
|
||||||
|
self, context, vnf_id, filters=None, fields=None):
|
||||||
|
vnf = self.get_device(context, vnf_id)
|
||||||
|
vnfd_tmpl = yaml.load(vnf['device_template']['attributes']['vnfd'])
|
||||||
|
policy_list = []
|
||||||
|
|
||||||
|
if vnfd_tmpl.get('tosca_definitions_version'):
|
||||||
|
polices = vnfd_tmpl['topology_template'].get('policies', [])
|
||||||
|
for policy_dict in polices:
|
||||||
|
for name, policy in policy_dict.items():
|
||||||
|
def _add(policy):
|
||||||
|
p = self._make_policy_dict(vnf, name, policy)
|
||||||
|
p['name'] = name
|
||||||
|
policy_list.append(p)
|
||||||
|
|
||||||
|
# Check for filters
|
||||||
|
if filters.get('name'):
|
||||||
|
if name == filters.get('name'):
|
||||||
|
_add(policy)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
|
|
||||||
|
_add(policy)
|
||||||
|
|
||||||
|
return policy_list
|
||||||
|
|
||||||
|
def get_vnf_policy(
|
||||||
|
self, context, policy_id, vnf_id, fields=None):
|
||||||
|
policies = self.get_vnf_policies(context,
|
||||||
|
vnf_id,
|
||||||
|
filters={'name': policy_id})
|
||||||
|
if policies:
|
||||||
|
return policies[0]
|
||||||
|
|
||||||
|
raise exceptions.VnfPolicyNotFound(policy=policy_id,
|
||||||
|
vnf_id=vnf_id)
|
||||||
|
|
||||||
|
def create_vnf_scale(self, context, vnf_id, scale):
|
||||||
|
policy_ = self.get_vnf_policy(context,
|
||||||
|
scale['scale']['policy'],
|
||||||
|
vnf_id)
|
||||||
|
policy_.update({'action': scale['scale']['type']})
|
||||||
|
self._handle_vnf_scaling(context, policy_)
|
||||||
|
|
||||||
|
return scale['scale']
|
||||||
|
Loading…
Reference in New Issue
Block a user