# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from unittest import mock

from neutron_lib.plugins import constants
from oslo_serialization import jsonutils
import webob

from gbpservice.neutron.services.servicechain.plugins.ncp import (
    plugin as ncp_plugin)
from gbpservice.neutron.services.servicechain.plugins.ncp import config  # noqa
from gbpservice.neutron.services.servicechain.plugins.ncp.node_drivers import (
    nfp_node_driver as nfp_node_driver)
from gbpservice.neutron.tests.unit.db.grouppolicy import test_group_policy_db
from gbpservice.neutron.tests.unit.services.grouppolicy import (
    test_resource_mapping as test_gp_driver)
from gbpservice.neutron.tests.unit.services.servicechain import (
    base_test_servicechain_plugin as test_base)
from gbpservice.neutron.tests.unit.services.servicechain.ncp import (
    test_ncp_plugin as test_ncp_plugin)
from gbpservice.nfp.orchestrator.db import nfp_db as nfp_db

SERVICE_DELETE_TIMEOUT = 15
SVC_MANAGEMENT_PTG = 'foo'


class ServiceChainNCPTestPlugin(ncp_plugin.NodeCompositionPlugin):

    supported_extension_aliases = ['servicechain'] + (
        test_group_policy_db.UNSUPPORTED_REQUIRED_EXTS)
    path_prefix = "/servicechain"


SC_PLUGIN_KLASS = (ServiceChainNCPTestPlugin.__module__ + '.' +
                   ServiceChainNCPTestPlugin.__name__)
CORE_PLUGIN = test_gp_driver.CORE_PLUGIN
GP_PLUGIN_KLASS = (
    "gbpservice.neutron.services.grouppolicy.plugin.GroupPolicyPlugin"
)


class NFPNodeDriverTestCase(
        test_base.BaseTestGroupPolicyPluginGroupResources,
        test_ncp_plugin.NodeCompositionPluginTestMixin):

    DEFAULT_VPN_CONFIG_DICT = {
            "heat_template_version": "2013-05-23",
            "description": "Creates new vpn service",
            "parameters": {
                "RouterId": {
                     "type": "string", "description": "Router ID"
                },
                "Subnet": {
                     "type": "string", "description": "Subnet id"
                },
                "ClientAddressPoolCidr": {
                     "type": "string", "description": "Pool"
                },
            },
            "resources": {
                "SSLVPNConnection": {
                    "type": "OS::Neutron::SSLVPNConnection",
                    "properties": {
                        "credential_id": "",
                        "client_address_pool_cidr": {
                            "get_param": "ClientAddressPoolCidr"
                        },
                        "name": "vtun0",
                        "vpnservice_id": {
                             "get_resource": "VPNService"
                        },
                        "admin_state_up": 'true'
                    }
                },
                "VPNService": {
                    "type": "OS::Neutron::VPNService",
                    "properties": {
                        "router_id": {
                            "get_param": "RouterId"
                        },
                        "subnet_id": {
                            "get_param": "Subnet"
                        },
                        "admin_state_up": 'true',
                        "name": "VPNService"
                    }
                }
            }
    }
    DEFAULT_VPN_CONFIG = jsonutils.dumps(DEFAULT_VPN_CONFIG_DICT)
    DEFAULT_LB_CONFIG_DICT = {
            "AWSTemplateFormatVersion": "2010-09-09",
            "Resources": {
                "test_pool": {
                    "Type": "OS::Neutron::Pool",
                    "Properties": {
                        "admin_state_up": True,
                        "description": "Haproxy pool from teplate",
                        "lb_method": "ROUND_ROBIN",
                        "monitors": [{"Ref": "HttpHM"}],
                        "name": "Haproxy pool",
                        "protocol": "HTTP",
                        "subnet_id": {"Ref": "Subnet"},
                        "vip": {
                            "subnet": {"Ref": "192.168.100.0"},
                            "address": {"Ref": "192.168.100.2"},
                            "name": "Haproxy vip",
                            "protocol_port": 80,
                            "connection_limit": -1,
                            "admin_state_up": True,
                            "description": "Haproxy vip from template"
                        }
                    }
                },
                "test_lb": {
                    "Type": "OS::Neutron::LoadBalancer",
                    "Properties": {
                        "pool_id": {"Ref": "HaproxyPool"},
                        "protocol_port": 80
                    }
                }
            }
    }
    DEFAULT_LB_CONFIG = jsonutils.dumps(DEFAULT_LB_CONFIG_DICT)
    DEFAULT_FW_CONFIG_DICT = {
            "heat_template_version": "2013-05-23",
            "resources": {
                'test_fw': {
                    "type": "OS::Neutron::Firewall",
                    "properties": {
                        "admin_state_up": True,
                        "firewall_policy_id": {
                            "get_resource": "Firewall_policy"},
                        "name": "testFirewall",
                        "description": "test Firewall"
                    }
                },
                'test_fw_policy': {
                    "type": "OS::Neutron::FirewallPolicy",
                    "properties": {
                        "shared": False,
                        "description": "test firewall policy",
                        "name": "testFWPolicy",
                        "firewall_rules": [{
                                "get_resource": "Rule_1"}],
                        "audited": True
                    }
                }
            }
    }
    DEFAULT_FW_CONFIG = jsonutils.dumps(DEFAULT_FW_CONFIG_DICT)
    SERVICE_PROFILE_VENDOR = 'NFP'

    def _create_service_profile(self, **kwargs):
        if not kwargs.get('insertion_mode'):
            kwargs['insertion_mode'] = 'l3'
        if not kwargs.get('service_flavor'):
            if kwargs['service_type'] == 'LOADBALANCERV2':
                kwargs['service_flavor'] = 'haproxy'
            else:
                kwargs['service_flavor'] = 'vyos'
        return super(NFPNodeDriverTestCase, self)._create_service_profile(
            **kwargs)

    def setUp(self):
        config.cfg.CONF.set_override('service_delete_timeout',
                                     SERVICE_DELETE_TIMEOUT,
                                     group='nfp_node_driver')

        config.cfg.CONF.set_override(
            'extension_drivers', ['proxy_group'], group='group_policy')
        config.cfg.CONF.set_override('node_drivers', ['nfp_node_driver'],
                                     group='node_composition_plugin')
        config.cfg.CONF.set_override('node_plumber', 'stitching_plumber',
                                     group='node_composition_plugin')
        config.cfg.CONF.set_override('policy_drivers',
                                     ['implicit_policy', 'resource_mapping',
                                      'chain_mapping'],
                                     group='group_policy')
        super(NFPNodeDriverTestCase, self).setUp(
            core_plugin=CORE_PLUGIN,
            gp_plugin=GP_PLUGIN_KLASS,
            sc_plugin=SC_PLUGIN_KLASS)

    def test_manager_initialized(self):
        mgr = self.plugin.driver_manager
        self.assertIsInstance(mgr.ordered_drivers[0].obj,
                              nfp_node_driver.NFPNodeDriver)
        for driver in mgr.ordered_drivers:
            self.assertTrue(driver.obj.initialized)

    def _nfp_create_profiled_servicechain_node(
            self, service_type=constants.LOADBALANCERV2, shared_profile=False,
            profile_tenant_id=None, profile_id=None,
            service_flavor=None, **kwargs):
        if not profile_id:
            prof = self.create_service_profile(
                service_type=service_type,
                shared=shared_profile,
                vendor=self.SERVICE_PROFILE_VENDOR,
                insertion_mode='l3', service_flavor='haproxy',
                tenant_id=profile_tenant_id or self._tenant_id)[
                                                    'service_profile']
        else:
            prof = self.get_service_profile(profile_id)
        service_config = kwargs.get('config')
        if not service_config or service_config == '{}':
            if service_type == constants.FIREWALL:
                kwargs['config'] = self.DEFAULT_FW_CONFIG
            else:
                kwargs['config'] = self.DEFAULT_LB_CONFIG
        return self.create_servicechain_node(
            service_profile_id=prof['id'], **kwargs)

    def _create_simple_fw_service_chain(self, number_of_nodes=1,
                                     service_type='FIREWALL'):
        prof = self.create_service_profile(
            service_type=service_type,
            vendor=self.SERVICE_PROFILE_VENDOR,
            insertion_mode='l3', service_flavor='vyos')['service_profile']
        node_ids = []
        for x in range(number_of_nodes):
            node_ids.append(self.create_servicechain_node(
                service_profile_id=prof['id'],
                config=self.DEFAULT_FW_CONFIG,
                expected_res_status=201)['servicechain_node']['id'])
        return self._nfp_create_chain_with_nodes(node_ids)

    def _nfp_create_chain_with_nodes(self, node_ids=None):
        node_ids = node_ids or []
        spec = self.create_servicechain_spec(
            nodes=node_ids,
            expected_res_status=201)['servicechain_spec']
        prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
        provider = self.create_policy_target_group(
            provided_policy_rule_sets={prs['id']: ''})['policy_target_group']
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                          "consumer_ptg_added_notification") as ptg_added:
            consumer = self.create_policy_target_group(
                consumed_policy_rule_sets={prs['id']: ''})[
                'policy_target_group']
            ptg_added.assert_called_once_with(mock.ANY,
                        mock.ANY, mock.ANY)
        return provider, consumer, prs

    def test_spec_parameters(self):
        pass

    def test_spec_ordering_list_servicechain_instances(self):
        pass


class DummyMap(object):
    network_function_id = '12'
    status = 'UP'


class TestServiceChainInstance(NFPNodeDriverTestCase):

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_node_create(self, plumbing_info):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "get_network_function") as get_nf:
                create_nf.return_value = {
                    'id': '126231632163'
                }
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'gateway'
                }
                self._create_simple_fw_service_chain()
                create_nf.assert_called_once_with(mock.ANY, mock.ANY)

    def _test_node_update(self):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "get_network_function") as get_nf:
                with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "update_service_config") as update_svc_config:
                    create_nf.return_value = {
                        'id': '126231632163'
                    }
                    get_nf.return_value = {
                        'id': '126231632163',
                        'status': 'ACTIVE'
                    }
                    prof = self.create_service_profile(
                                service_type=constants.FIREWALL,
                                vendor=self.SERVICE_PROFILE_VENDOR,
                                insertion_mode='l3',
                                service_flavor='vyos')['service_profile']

                    self.create_policy_target_group(
                                name='foo')['policy_target_group']
                    node = self.create_servicechain_node(
                                service_profile_id=prof['id'],
                                config=self.DEFAULT_FW_CONFIG,
                                expected_res_status=201)['servicechain_node']

                    self._nfp_create_chain_with_nodes(node_ids=[node['id']])
                    self.update_servicechain_node(
                                node['id'],
                                name='newname',
                                expected_res_status=200)
                    create_nf.assert_called_once_with(mock.ANY, mock.ANY)
                    update_svc_config.assert_called_once_with()

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_node_delete(self, plumbing_info):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }

                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'gateway'
                }
                prof = self.create_service_profile(
                                service_type=constants.FIREWALL,
                                vendor=self.SERVICE_PROFILE_VENDOR,
                                insertion_mode='l3',
                                service_flavor='vyos')['service_profile']
                node_id = self.create_servicechain_node(
                                service_profile_id=prof['id'],
                                config=self.DEFAULT_FW_CONFIG,
                                expected_res_status=201)['servicechain_node'][
                                'id']

                spec = self.create_servicechain_spec(
                    nodes=[node_id],
                    expected_res_status=201)['servicechain_spec']
                prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
                provider = self.create_policy_target_group(
                    provided_policy_rule_sets={prs['id']: ''})[
                    'policy_target_group']
                create_nf.assert_called_once_with(mock.ANY, mock.ANY)
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "get_network_function") as get_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                    "delete_network_function") as delete_nf,\
                mock.patch.object(nfp_db.NFPDbBase,
                    "get_node_instance_network_function_map") as get_map,\
                mock.patch.object(nfp_db.NFPDbBase,
                    "update_node_instance_network_function_map") as update_map:

                get_map.return_value = DummyMap()
                update_map.return_value = mock.ANY
                get_nf.return_value = None
                self.delete_policy_target_group(
                      provider['id'], expected_res_status=204)
                delete_nf.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_wait_for_network_function_delete_completion(self, plumbing_info):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'gateway'
                }
                prof = self.create_service_profile(
                                service_type=constants.FIREWALL,
                                vendor=self.SERVICE_PROFILE_VENDOR,
                                insertion_mode='l3',
                                service_flavor='vyos')['service_profile']
                node_id = self.create_servicechain_node(
                                service_profile_id=prof['id'],
                                config=self.DEFAULT_FW_CONFIG,
                                expected_res_status=201)['servicechain_node'][
                                'id']

                spec = self.create_servicechain_spec(
                                nodes=[node_id],
                                expected_res_status=201)['servicechain_spec']
                prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
                provider = self.create_policy_target_group(
                                provided_policy_rule_sets={prs['id']: ''})[
                                'policy_target_group']
                create_nf.assert_called_once_with(mock.ANY, mock.ANY)

            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'delete_network_function') as delete_nf:
                with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf,\
                    mock.patch.object(nfp_db.NFPDbBase,
                        "get_node_instance_network_function_map") as get_map,\
                    mock.patch.object(nfp_db.NFPDbBase,
                        "update_node_instance_network_function_map") as \
                        update_map:

                    get_map.return_value = DummyMap()
                    update_map.return_value = mock.ANY
                    delete_nf.return_value = None
                    get_nf.return_value = None
                    # Removing the PRSs will make the PTG deletable again
                    self.update_policy_target_group(
                                       provider['id'],
                                       provided_policy_rule_sets={},
                                       expected_res_status=200)
                    self.delete_policy_target_group(provider['id'],
                                       expected_res_status=204)
                    delete_nf.assert_called_once_with(mock.ANY, mock.ANY,
                            mock.ANY)

    def _create_policy_target_port(self, policy_target_group_id):
        pt = self.create_policy_target(
                policy_target_group_id=policy_target_group_id)['policy_target']
        req = self.new_show_request('ports', pt['port_id'], fmt=self.fmt)
        port = self.deserialize(self.fmt,
                                req.get_response(self.api))['port']
        return (pt, port)

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_lb_node_create(self, plumbing_info, consumer_external=False):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'endpoint'
                }
                node_id = self._nfp_create_profiled_servicechain_node(
                    service_type=constants.LOADBALANCERV2)[
                            'servicechain_node']['id']
                spec = self.create_servicechain_spec(
                    nodes=[node_id],
                    expected_res_status=201)['servicechain_spec']

                prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
                params = [{'type': 'ip_single', 'name': 'vip_ip',
                           'value': 'self_subnet'}]

                nsp = self.create_network_service_policy(
                           network_service_params=params)
                network_service_policy_id = nsp['network_service_policy']['id']
                provider = self.create_policy_target_group(
                           network_service_policy_id=network_service_policy_id,
                           provided_policy_rule_sets={prs['id']: ''})[
                           'policy_target_group']

                with mock.patch.object(nfp_node_driver.NFPClientApi,
                          "policy_target_added_notification") as pt_added:
                    # Verify notification issued for created PT in the provider
                    _, port = self._create_policy_target_port(provider['id'])
                    pt_added.assert_called_once_with(mock.ANY, mock.ANY,
                           mock.ANY)

                if consumer_external:
                    self._create_external_policy(prs['id'])
                else:
                    self.create_policy_target_group(
                          consumed_policy_rule_sets={prs['id']: ''})

                create_nf.assert_called_once_with(mock.ANY, mock.ANY)

    def test_invalid_service_type_rejected(self):
        node_used = self._nfp_create_profiled_servicechain_node(
            service_type="test")['servicechain_node']
        spec_used = self.create_servicechain_spec(
            nodes=[node_used['id']])['servicechain_spec']
        provider = self.create_policy_target_group()['policy_target_group']
        classifier = self.create_policy_classifier()['policy_classifier']
        res = self.create_servicechain_instance(
            provider_ptg_id=provider['id'],
            classifier_id=classifier['id'],
            servicechain_specs=[spec_used['id']],
            expected_res_status=webob.exc.HTTPBadRequest.code)
        self.assertEqual('NoDriverAvailableForAction',
                         res['NeutronError']['type'])

    def test_is_node_order_in_spec_supported(self):
        lb_prof = self.create_service_profile(
                    service_type=constants.LOADBALANCERV2,
                    vendor=self.SERVICE_PROFILE_VENDOR,
                    insertion_mode='l3',
                    service_flavor='haproxy')['service_profile']
        vpn_prof = self.create_service_profile(
                    service_type=constants.VPN,
                    vendor=self.SERVICE_PROFILE_VENDOR,
                    insertion_mode='l3',
                    service_flavor='vyos')['service_profile']
        vpn_node = self.create_servicechain_node(
                    service_profile_id=vpn_prof['id'],
                    config=self.DEFAULT_VPN_CONFIG,
                    expected_res_status=201)['servicechain_node']
        lb_node = self.create_servicechain_node(
                    service_profile_id=lb_prof['id'],
                    config=self.DEFAULT_LB_CONFIG,
                    expected_res_status=201)['servicechain_node']
        node_ids = [lb_node['id'], vpn_node['id']]
        spec = self.create_servicechain_spec(
                    nodes=node_ids,
                    expected_res_status=201)['servicechain_spec']

        provider = self.create_policy_target_group()['policy_target_group']
        classifier = self.create_policy_classifier()['policy_classifier']
        res = self.create_servicechain_instance(
                    provider_ptg_id=provider['id'],
                    classifier_id=classifier['id'],
                    servicechain_specs=[spec['id']],
                    expected_res_status=webob.exc.HTTPBadRequest.code)
        self.assertEqual('NoDriverAvailableForAction',
                    res['NeutronError']['type'])

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_validate_update(self, plumbing_info):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "get_network_function") as get_nf:
                create_nf.return_value = {
                    'id': '126231632163'
                }
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'gateway'
                }
                fw_prof = self.create_service_profile(
                            service_type=constants.FIREWALL,
                            vendor=self.SERVICE_PROFILE_VENDOR,
                            insertion_mode='l3',
                            service_flavor='vyos')['service_profile']
                fw_node = self.create_servicechain_node(
                            service_profile_id=fw_prof['id'],
                            config=self.DEFAULT_FW_CONFIG,
                            expected_res_status=201)['servicechain_node']
                node_ids = [fw_node['id']]
                spec = self.create_servicechain_spec(
                            nodes=node_ids,
                            expected_res_status=201)['servicechain_spec']
                provider = self.create_policy_target_group()[
                            'policy_target_group']
                classifier = self.create_policy_classifier()[
                            'policy_classifier']
                servicechain_instance = self.create_servicechain_instance(
                            provider_ptg_id=provider['id'],
                            classifier_id=classifier['id'],
                            servicechain_specs=[spec['id']])[
                            'servicechain_instance']
                fw_prof = self.create_service_profile(
                            service_type='test',
                            vendor=self.SERVICE_PROFILE_VENDOR,
                            insertion_mode='l3',
                            service_flavor='vyos')['service_profile']
                fw_node = self.create_servicechain_node(
                            service_profile_id=fw_prof['id'],
                            config=self.DEFAULT_FW_CONFIG,
                            expected_res_status=201)['servicechain_node']
                node_ids = [fw_node['id']]
                spec = self.create_servicechain_spec(
                            nodes=node_ids,
                            expected_res_status=201)['servicechain_spec']
                create_nf.assert_called_once_with(mock.ANY, mock.ANY)
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "get_network_function") as get_nf:
                with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "delete_network_function") as delete_nf,\
                    mock.patch.object(nfp_db.NFPDbBase,
                        "get_node_instance_network_function_map") as get_map,\
                    mock.patch.object(nfp_db.NFPDbBase,
                        "update_node_instance_network_function_map") as \
                        update_map:
                    get_map.return_value = DummyMap()
                    update_map.return_value = mock.ANY
                    get_nf.return_value = None
                    res = self.update_servicechain_instance(
                            servicechain_instance['id'],
                            servicechain_specs=[spec['id']],
                            expected_res_status=webob.exc.HTTPBadRequest.code)
                    delete_nf.assert_called_once_with(mock.ANY,
                            mock.ANY, mock.ANY)
                    self.assertEqual('NoDriverAvailableForAction',
                            res['NeutronError']['type'])

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_update_node_consumer_ptg_added(self, plumbing_info):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'gateway'
                }
                prof = self.create_service_profile(
                                service_type=constants.FIREWALL,
                                vendor=self.SERVICE_PROFILE_VENDOR,
                                insertion_mode='l3',
                                service_flavor='vyos')['service_profile']
                node_id = self.create_servicechain_node(
                                service_profile_id=prof['id'],
                                config=self.DEFAULT_FW_CONFIG,
                                expected_res_status=201)['servicechain_node'][
                                'id']

                spec = self.create_servicechain_spec(
                    nodes=[node_id],
                    expected_res_status=201)['servicechain_spec']
                prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
                self.create_policy_target_group(
                    provided_policy_rule_sets={prs['id']: ''})[
                    'policy_target_group']
                create_nf.assert_called_once_with(mock.ANY, mock.ANY)
                with mock.patch.object(nfp_node_driver.NFPClientApi,
                          "consumer_ptg_added_notification") as ptg_added:
                    self.create_policy_target_group(
                        consumed_policy_rule_sets={prs['id']: ''})[
                        'policy_target_group']
                    ptg_added.assert_called_once_with(mock.ANY,
                        mock.ANY, mock.ANY)

    def _test_update_node_consumer_ptg_removed(self):
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }

                prof = self.create_service_profile(
                                service_type=constants.FIREWALL,
                                vendor=self.SERVICE_PROFILE_VENDOR,
                                insertion_mode='l3',
                                service_flavor='vyos')['service_profile']
                node_id = self.create_servicechain_node(
                                service_profile_id=prof['id'],
                                config=self.DEFAULT_FW_CONFIG,
                                expected_res_status=201)['servicechain_node'][
                                'id']

                spec = self.create_servicechain_spec(
                    nodes=[node_id],
                    expected_res_status=201)['servicechain_spec']
                prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
                self.create_policy_target_group(
                    provided_policy_rule_sets={prs['id']: ''})[
                    'policy_target_group']
                with mock.patch.object(nfp_node_driver.NFPClientApi,
                    "consumer_ptg_added_notification") as ptg_added:
                    consumer = self.create_policy_target_group(
                        consumed_policy_rule_sets={prs['id']: ''})[
                        'policy_target_group']
                    ptg_added.assert_called_once_with(mock.ANY, mock.ANY,
                        mock.ANY)
                create_nf.assert_called_once_with(mock.ANY, mock.ANY)

        with mock.patch.object(nfp_node_driver.NFPClientApi,
            "consumer_ptg_removed_notification") as ptg_removed:
            self.delete_policy_target_group(
                    consumer['id'], expected_res_status=204)
            ptg_removed.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY)

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_policy_target_add_remove(self, plumbing_info):
        prof = self._create_service_profile(
            service_type='LOADBALANCERV2',
            vendor=self.SERVICE_PROFILE_VENDOR,
            insertion_mode='l3', service_flavor='haproxy')['service_profile']
        node = self.create_servicechain_node(
            service_profile_id=prof['id'],
            config=self.DEFAULT_LB_CONFIG,
            expected_res_status=201)['servicechain_node']

        spec = self.create_servicechain_spec(
            nodes=[node['id']],
            expected_res_status=201)['servicechain_spec']
        prs = self._create_redirect_prs(spec['id'])['policy_rule_set']
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'endpoint'
                }
                params = [{'type': 'ip_single', 'name': 'vip_ip',
                           'value': 'self_subnet'}]
                nsp = self.create_network_service_policy(
                           network_service_params=params)
                network_service_policy_id = nsp['network_service_policy'][
                           'id']
                provider = self.create_policy_target_group(
                      network_service_policy_id=network_service_policy_id,
                      provided_policy_rule_sets={prs['id']: ''})[
                      'policy_target_group']
                self.create_policy_target_group(
                      consumed_policy_rule_sets={prs['id']: ''})

                with mock.patch.object(nfp_node_driver.NFPClientApi,
                          "policy_target_added_notification") as pt_added:
                    # Verify notification issued for created PT in the provider
                    pt = self.create_policy_target(
                         policy_target_group_id=provider['id'])[
                         'policy_target']
                    create_nf.assert_called_once_with(mock.ANY, mock.ANY)
                    pt_added.assert_called_once_with(mock.ANY, mock.ANY,
                            mock.ANY)

        # Verify notification issued for deleted PT in the provider
        with mock.patch.object(nfp_node_driver.NFPClientApi,
                          "policy_target_removed_notification") as pt_removed:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                self.delete_policy_target(pt['id'])
                pt_removed.assert_called_once_with(mock.ANY, mock.ANY,
                                                   mock.ANY)

    @mock.patch.object(nfp_node_driver.NFPClientApi, 'get_plumbing_info')
    def test_policy_target_group_updated(self, plumbing_info):
        prof = self._create_service_profile(
                service_type='FIREWALL',
                vendor=self.SERVICE_PROFILE_VENDOR,
                insertion_mode='l3', service_flavor='vyos')['service_profile']
        node = self.create_servicechain_node(
                service_profile_id=prof['id'],
                config=self.DEFAULT_FW_CONFIG,
                expected_res_status=201)['servicechain_node']
        spec = self.create_servicechain_spec(
                nodes=[node['id']])['servicechain_spec']

        action = self.create_policy_action(
                action_type='REDIRECT', action_value=spec['id'])[
            'policy_action']
        classifier = self.create_policy_classifier(
                direction='bi', protocol='icmp')[
            'policy_classifier']
        rule = self.create_policy_rule(
                policy_classifier_id=classifier['id'],
                policy_actions=[action['id']])['policy_rule']
        prs = self.create_policy_rule_set(
                policy_rules=[rule['id']])['policy_rule_set']
        # allow
        allow_action = self.create_policy_action(action_type='ALLOW')[
            'policy_action']
        allow_rule = self.create_policy_rule(
                policy_classifier_id=classifier['id'],
                policy_actions=[allow_action['id']])['policy_rule']
        allow_prs = self.create_policy_rule_set(
                policy_rules=[allow_rule['id']])['policy_rule_set']
        # ref ptg
        ref_ptg = self.create_policy_target_group()['policy_target_group']
        ref_pt = self.create_policy_target(
                policy_target_group_id=ref_ptg['id'])['policy_target']

        with mock.patch.object(nfp_node_driver.NFPClientApi,
                               "create_network_function") as create_nf:
            with mock.patch.object(nfp_node_driver.NFPClientApi,
                                   'get_network_function') as get_nf:
                get_nf.return_value = {
                    'id': '126231632163',
                    'status': 'ACTIVE'
                }
                create_nf.return_value = {
                    'id': '126231632163'
                }
                plumbing_info.return_value = {
                    'management': [],
                    'provider': [{}],
                    'consumer': [{}],
                    'plumbing_type': 'gateway'
                }
                orig_ptg = self.create_policy_target_group(
                        description="opflex_eoc:%s" % ref_pt['port_id'],
                        provided_policy_rule_sets={prs['id']: ''})[
                    'policy_target_group']
                current_ptg = self.update_policy_target_group(
                        orig_ptg['id'],
                        provided_policy_rule_sets={
                            prs['id']: '', allow_prs['id']: ''})[
                    'policy_target_group']
                ref_ptg = self.show_policy_target_group(ref_ptg['id'])[
                    'policy_target_group']
                self.assertSetEqual(set(ref_ptg['provided_policy_rule_sets']),
                                    set(current_ptg[
                                            'provided_policy_rule_sets']))
