VNF scaling: REST API

implments blueprint: #vnf-scaling

Change-Id: Ib8cdd1295460e617806eec173e4d4ed8f35e6642
This commit is contained in:
Kanagaraj Manickam 2016-06-22 12:22:47 +05:30
parent 89cb3c4fbc
commit ace3f15c56
5 changed files with 282 additions and 4 deletions

View File

@ -243,3 +243,17 @@ class InvalidCIDR(BadRequest):
class MgmtDriverException(TackerException):
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")

View File

@ -402,6 +402,19 @@ class VNFMPluginDb(vnfm.VNFMPluginBase, db_base.CommonDbMixin):
device_db.update({'status': new_status})
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):
with context.session.begin(subtransactions=True):
device_db = self._get_device_db(

View File

@ -20,8 +20,10 @@ import six
from tacker.api import extensions
from tacker.api.v1 import attributes as attr
from tacker.api.v1 import base
from tacker.api.v1 import resource_helper
from tacker.common import exceptions
from tacker import manager
from tacker.plugins.common import constants
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):
@classmethod
def get_name(cls):
@ -333,9 +370,31 @@ class Vnfm(extensions.ExtensionDescriptor):
special_mappings, RESOURCE_ATTRIBUTE_MAP)
plural_mappings['service_types'] = 'service_type'
attr.PLURALS.update(plural_mappings)
return resource_helper.build_resource_info(
resources = resource_helper.build_resource_info(
plural_mappings, RESOURCE_ATTRIBUTE_MAP, constants.VNFM,
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
def get_plugin_interface(cls):
@ -397,3 +456,8 @@ class VNFMPluginBase(service_base.NFVPluginBase):
@abc.abstractmethod
def delete_vnf(self, context, vnf_id):
pass
@abc.abstractmethod
def create_vnf_scale(
self, context, vnf_id, scale):
pass

View File

@ -29,9 +29,13 @@ COMMON_PREFIXES = {
# Service operation status constants
ACTIVE = "ACTIVE"
DOWN = "DOWN"
PENDING_CREATE = "PENDING_CREATE"
PENDING_UPDATE = "PENDING_UPDATE"
PENDING_DELETE = "PENDING_DELETE"
PENDING_SCALE_IN = "PENDING_SCALE_IN"
PENDING_SCALE_OUT = "PENDING_SCALE_OUT"
INACTIVE = "INACTIVE"
DEAD = "DEAD"
ERROR = "ERROR"
@ -41,3 +45,8 @@ ACTIVE_PENDING_STATUSES = (
PENDING_CREATE,
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}

View File

@ -17,6 +17,7 @@
import copy
import inspect
import six
import yaml
import eventlet
from oslo_config import cfg
@ -27,7 +28,7 @@ from oslo_utils import excutils
from tacker._i18n import _LE
from tacker.api.v1 import attributes
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.extensions import vnfm
from tacker.plugins.common import constants
@ -243,7 +244,7 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
new_status = constants.ACTIVE
try:
self.mgmt_call(context, device_dict, kwargs)
except MgmtDriverException:
except exceptions.MgmtDriverException:
LOG.error(_('VNF configuration failed'))
new_status = constants.ERROR
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,
region_name=region_name)
self.mgmt_call(context, device_dict, kwargs)
except MgmtDriverException as e:
except exceptions.MgmtDriverException as e:
LOG.error(_('VNF configuration failed'))
new_status = constants.ERROR
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)
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):
vnf['device'] = vnf.pop('vnf')
vnf_attributes = vnf['device']
@ -436,3 +558,59 @@ class VNFMPlugin(vm_db.VNFMPluginDb, VNFMMgmtMixin):
vnfd['device_template'] = vnfd.pop('vnfd')
new_dict = self.create_device_template(context, vnfd)
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']