Support CLI of Change External VNF Connectivity

Add ``openstack vnflcm op chg-ext-conn`` to python-tackerclient.
This command can execute Change external VNF Connectivity operation.
This API can change VL setting.

Implements: blueprint support-change-external-connectivity
Spec: https://specs.openstack.org/openstack/tacker-specs/specs/wallaby/support-change-external-VNF-connectivity-operation.html
Change-Id: I3a935296646361032665082a4060bc21ff51c1b1
This commit is contained in:
Wataru Juso
2021-02-18 00:36:24 +09:00
committed by Aldinson Esto
parent 58167535c7
commit cfe14110d1
5 changed files with 212 additions and 0 deletions

View File

@@ -94,5 +94,6 @@ openstack.tackerclient.v1 =
vnflcm_heal = tackerclient.osc.v1.vnflcm.vnflcm:HealVnfLcm
vnflcm_update = tackerclient.osc.v1.vnflcm.vnflcm:UpdateVnfLcm
vnflcm_scale = tackerclient.osc.v1.vnflcm.vnflcm:ScaleVnfLcm
vnflcm_change-ext-conn = tackerclient.osc.v1.vnflcm.vnflcm:ChangeExtConnVnfLcm
vnflcm_op_rollback = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:RollbackVnfLcmOp
vnflcm_op_fail = tackerclient.osc.v1.vnflcm.vnflcm_op_occs:FailVnfLcmOp

View File

@@ -0,0 +1,69 @@
{
"extVirtualLinks": [
{
"id": "ext-vl-uuid-VL1",
"resourceId": "neutron-network-uuid_VL1",
"extCps": [
{
"cpdId": "CP1",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"numDynamicAddresses": 1,
"subnetId": "subnet-uuid"
}
]
}
}
]
}
]
},
{
"cpdId": "CP2",
"cpConfig": [
{
"cpProtocolData": [
{
"layerProtocol": "IP_OVER_ETHERNET",
"ipOverEthernet": {
"ipAddresses": [
{
"type": "IPV4",
"fixedAddresses": [
"10.0.0.1"
],
"subnetId": "subnet-uuid"
}
]
}
}
]
}
]
}
]
}
],
"vimConnectionInfo": [
{
"id": "vim-uuid",
"vimType": "ETSINFV.OPENSTACK_KEYSTONE.v_2",
"vimConnectionId": "dummy-vimid",
"interfaceInfo": {
"key1":"value1",
"key2":"value2"
},
"accessInfo": {
"key1":"value1",
"key2":"value2"
}
}
]
}

View File

@@ -537,3 +537,30 @@ class ScaleVnfLcm(command.Command):
if not result:
print((_('Scale request for VNF Instance %s has been accepted.')
% parsed_args.vnf_instance))
class ChangeExtConnVnfLcm(command.Command):
_description = _("Change External VNF Connectivity")
def get_parser(self, prog_name):
parser = super(ChangeExtConnVnfLcm, self).get_parser(prog_name)
parser.add_argument(
_VNF_INSTANCE,
metavar="<vnf-instance>",
help=_("VNF instance ID to Change External VNF Connectivity"))
parser.add_argument(
'request_file',
metavar="<param-file>",
help=_("Specify change-ext-conn request parameters "
"in a json file."))
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.tackerclient
result = client.change_ext_conn_vnf_instance(
parsed_args.vnf_instance, jsonfile2body(
parsed_args.request_file))
if not result:
print((_('Change External VNF Connectivity for VNF Instance %s '
'has been accepted.') % parsed_args.vnf_instance))

View File

@@ -739,3 +739,110 @@ class TestScaleVnfLcm(TestVnfLcm):
self.assertRaises(exceptions.TackerClientException,
self.scale_vnf_lcm.take_action,
parsed_args)
class TestChangeExtConnVnfLcm(TestVnfLcm):
def setUp(self):
super(TestChangeExtConnVnfLcm, self).setUp()
self.change_ext_conn_vnf_lcm = vnflcm.ChangeExtConnVnfLcm(
self.app, self.app_args,
cmd_name='vnflcm change-ext-conn')
def test_take_action(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"change_ext_conn_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.change_ext_conn_vnf_lcm,
arglist,
verifylist)
url = os.path.join(self.url, 'vnflcm/v1/vnf_instances',
vnf_instance['id'], 'change_ext_conn')
self.requests_mock.register_uri(
'POST', url, headers=self.header, json={})
sys.stdout = buffer = StringIO()
with mock.patch.object(proxy_client.ClientBase,
'_handle_fault_response') as m:
self.change_ext_conn_vnf_lcm.take_action(parsed_args)
# check no fault response is received
self.assertNotCalled(m)
self.assertEqual(
('Change External VNF Connectivity for VNF Instance {0} '
'has been accepted.'.format(vnf_instance['id'])),
buffer.getvalue().strip())
def test_take_action_vnf_instance_not_found(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = ("./tackerclient/osc/v1/vnflcm/samples/"
"change_ext_conn_vnf_instance_param_sample.json")
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(self.change_ext_conn_vnf_lcm,
arglist,
verifylist)
url = os.path.join(self.url, 'vnflcm/v1/vnf_instances',
vnf_instance['id'], 'change_ext_conn')
self.requests_mock.register_uri(
'POST', url, headers=self.header, status_code=404, json={})
self.assertRaises(exceptions.TackerClientException,
self.change_ext_conn_vnf_lcm.take_action,
parsed_args)
def test_take_action_param_file_not_exists(self):
vnf_instance = vnflcm_fakes.vnf_instance_response()
sample_param_file = "./not_exists.json"
arglist = [vnf_instance['id'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
# command param
parsed_args = self.check_parser(
self.change_ext_conn_vnf_lcm,
arglist,
verifylist)
ex = self.assertRaises(
exceptions.InvalidInput,
self.change_ext_conn_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))
@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'], sample_param_file]
verifylist = [('vnf_instance', vnf_instance['id']),
('request_file', sample_param_file)]
mock_open.return_value = "invalid_json_data"
# command param
parsed_args = self.check_parser(self.change_ext_conn_vnf_lcm,
arglist,
verifylist)
ex = self.assertRaises(
exceptions.InvalidInput,
self.change_ext_conn_vnf_lcm.take_action,
parsed_args)
expected_msg = "Failed to load parameter file."
self.assertIn(expected_msg, str(ex))

View File

@@ -931,6 +931,11 @@ class VnfLCMClient(ClientBase):
def fail_vnf_instance(self, occ_id):
return self.post((self.vnf_lcm_op_occs_path + "/fail") % occ_id)
@APIParamsCall
def change_ext_conn_vnf_instance(self, vnf_id, body):
return self.post((self.vnf_instance_path + "/change_ext_conn") %
vnf_id, body=body)
class Client(object):
"""Unified interface to interact with multiple applications of tacker service.
@@ -1204,6 +1209,9 @@ class Client(object):
def scale_vnf_instance(self, vnf_id, body):
return self.vnf_lcm_client.scale_vnf_instance(vnf_id, body)
def change_ext_conn_vnf_instance(self, vnf_id, body):
return self.vnf_lcm_client.change_ext_conn_vnf_instance(vnf_id, body)
def delete_vnf_instance(self, vnf_id):
return self.vnf_lcm_client.delete_vnf_instance(vnf_id)