Support of Scale command in openstackclient

Supported Scale command in Openstack Tacker Client.

Implements: blueprint support-etsi-nfv-specs

Change-Id: I27b670c0aaa0c9b9cbce6ae44e03e5ba6234beb5
This commit is contained in:
Aldinson Esto
2020-08-21 17:47:15 +09:00
committed by Koichi Edagawa
parent 60c268f04d
commit d7f7ea35d2
5 changed files with 165 additions and 29 deletions

View File

@@ -92,3 +92,4 @@ openstack.tackerclient.v1 =
vnflcm_delete = tackerclient.osc.v1.vnflcm.vnflcm:DeleteVnfLcm
vnflcm_heal = tackerclient.osc.v1.vnflcm.vnflcm:HealVnfLcm
vnflcm_update = tackerclient.osc.v1.vnflcm.vnflcm:UpdateVnfLcm
vnflcm_scale = tackerclient.osc.v1.vnflcm.vnflcm:ScaleVnfLcm

View File

@@ -0,0 +1,3 @@
{
"additionalParams": {"key1":"value1", "key2":"value2"}
}

View File

@@ -465,3 +465,71 @@ class UpdateVnfLcm(command.Command):
if not result:
print((_('Update vnf:%(id)s ') %
{'id': parsed_args.vnf_instance}))
class ScaleVnfLcm(command.Command):
_description = _("Scale a VNF Instance")
def get_parser(self, prog_name):
parser = super(ScaleVnfLcm, self).get_parser(prog_name)
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
help=_('VNF instance ID to scale'))
parser.add_argument(
'--I',
metavar="<param-file>",
help=_("Specify scale request parameters in a json file."))
parser.add_argument(
'--type',
metavar="<type>",
choices=['SCALE_OUT', 'SCALE_IN'],
help=_("Indicates the type of the scale operation requested"))
parser.add_argument(
'--aspect-id',
metavar="<aspect-id>",
help=_("Identifier of the scaling aspect."))
parser.add_argument(
'--number-of-steps',
metavar="<number-of-steps>",
type=int,
help=_("Number of scaling steps to be executed as part of"
"this Scale VNF operation."))
parser.add_argument(
'--additional-param-file',
metavar="<additional-param-file>",
help=_("Additional parameters passed by the NFVO as input"
"to the scaling process."))
return parser
def args2body(self, file_path=None):
"""To store request body, call jsonfile2body.
Args:
file_path ([string], optional): file path of param file(json).
Defaults to None.
Returns:
body[dict]: [description]
"""
body = {}
if file_path:
return jsonfile2body(file_path)
return body
def take_action(self, parsed_args):
"""Execute scale_vnf_instance and output result comment.
Args:
parsed_args ([Namespace]): [description]
"""
client = self.app.client_manager.tackerclient
if parsed_args.additional_param_file:
result = client.scale_vnf_instance(
parsed_args.vnf_instance,
self.args2body(file_path=parsed_args.additional_param_file))
if not result:
print((_('Scale request for VNF Instance %(id)s has been'
' accepted.') % {'id': parsed_args.vnf_instance}))

View File

@@ -602,53 +602,109 @@ class TestUpdateVnfLcm(TestVnfLcm):
parsed_args = self.check_parser(self.update_vnf_lcm, arglist,
verifylist)
self.assertRaises(exceptions.InvalidInput,
self.update_vnf_lcm.take_action, parsed_args)
@ddt.ddt
class TestScaleVnfLcm(TestVnfLcm):
def setUp(self):
super(TestScaleVnfLcm, self).setUp()
self.scale_vnf_lcm = vnflcm.ScaleVnfLcm(
self.app, self.app_args, cmd_name='vnflcm scale')
@ddt.data('SCALE_IN', 'SCALE_OUT')
def test_take_action(self, scale_type):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"scale_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'],
'--aspect-id', uuidsentinel.aspect_id,
'--number-of-steps', '1',
'--type', scale_type,
'--additional-param-file', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('aspect_id', uuidsentinel.aspect_id),
('number_of_steps', 1),
('type', scale_type),
('additional_param_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.scale_vnf_lcm, arglist,
verifylist)
url = os.path.join(
self.url,
'vnflcm/v1/vnf_instances',
vnf_instance['id'],
'scale')
self.requests_mock.register_uri(
'POST', url, headers=self.header, json={})
sys.stdout = buffer = StringIO()
self.scale_vnf_lcm.take_action(parsed_args)
actual_message = buffer.getvalue().strip()
expected_message = ("Scale request for VNF Instance %s has been "
"accepted.") % vnf_instance['id']
self.assertEqual(expected_message, actual_message)
@ddt.data('SCALE_IN', 'SCALE_OUT')
def test_take_action_param_file_not_exists(self, scale_type):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./not_exists.json"
arglist = [vnf_instance['id'],
'--aspect-id', uuidsentinel.aspect_id,
'--number-of-steps', '2',
'--type', scale_type,
'--additional-param-file', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('aspect_id', uuidsentinel.aspect_id),
('number_of_steps', 2),
('type', scale_type),
('additional_param_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.scale_vnf_lcm, arglist,
verifylist)
ex = self.assertRaises(exceptions.InvalidInput,
self.update_vnf_lcm.take_action, parsed_args)
self.scale_vnf_lcm.take_action, parsed_args)
expected_msg = ("Invalid input: File %s does not exist "
"or user does not have read privileges to it")
self.assertEqual(expected_msg % sample_param_file, str(ex))
def test_take_action_vnf_instance_not_found(self):
@ddt.data('SCALE_IN', 'SCALE_OUT')
def test_take_action_vnf_instance_not_found(self, scale_type):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"update_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'], '--I', sample_param_file]
arglist = [vnf_instance['id'],
'--aspect-id', uuidsentinel.aspect_id,
'--number-of-steps', '3',
'--type', scale_type,
'--additional-param-file', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('I', sample_param_file)]
('aspect_id', uuidsentinel.aspect_id),
('number_of_steps', 3),
('type', scale_type),
('additional_param_file', sample_param_file)]
# command param
parsed_args = self.check_parser(
self.update_vnf_lcm, arglist, verifylist)
parsed_args = self.check_parser(self.scale_vnf_lcm, arglist,
verifylist)
url = os.path.join(
self.url,
'vnflcm/v1/vnf_instances',
vnf_instance['id'])
self.requests_mock.register_uri(
'PATCH', url, headers=self.header, status_code=404, json={})
'POST', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.update_vnf_lcm.take_action,
self.scale_vnf_lcm.take_action,
parsed_args)
@mock.patch("os.open")
@mock.patch("os.access")
def test_take_action_invalid_format_param_file(self, mock_open,
mock_access):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./invalid_param_file.json"
arglist = [vnf_instance['id'], '--I', sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('I', sample_param_file)]
mock_open.return_value = "invalid_json_data"
# command param
parsed_args = self.check_parser(self.update_vnf_lcm, arglist,
verifylist)
ex = self.assertRaises(exceptions.InvalidInput,
self.update_vnf_lcm.take_action,
parsed_args)
expected_msg = "Failed to load parameter file."
self.assertIn(expected_msg, str(ex))

View File

@@ -900,6 +900,11 @@ class VnfLCMClient(ClientBase):
def update_vnf_instance(self, vnf_id, body):
return self.patch(self.vnf_instance_path % vnf_id, body=body)
@APIParamsCall
def scale_vnf_instance(self, vnf_id, body):
return self.post((self.vnf_instance_path + "/scale") % vnf_id,
body=body)
class Client(object):
"""Unified interface to interact with multiple applications of tacker service.
@@ -1170,6 +1175,9 @@ class Client(object):
def terminate_vnf_instance(self, vnf_id, body):
return self.vnf_lcm_client.terminate_vnf_instance(vnf_id, body)
def scale_vnf_instance(self, vnf_id, body):
return self.vnf_lcm_client.scale_vnf_instance(vnf_id, body)
def delete_vnf_instance(self, vnf_id):
return self.vnf_lcm_client.delete_vnf_instance(vnf_id)