# Copyright 2013 Radware LTD.
#
#    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.

import re

import contextlib
import mock
from neutron.api.v2 import attributes
from neutron import context
from neutron import manager
from neutron.plugins.common import constants
from oslo_config import cfg
from oslo_serialization import jsonutils
from six.moves import queue as Queue

from neutron_lbaas.extensions import loadbalancer
from neutron_lbaas.services.loadbalancer.drivers.radware import driver
from neutron_lbaas.services.loadbalancer.drivers.radware \
    import exceptions as r_exc
from neutron_lbaas.tests.unit.db.loadbalancer import test_db_loadbalancer

GET_200 = ('/api/workflow/', '/api/service/', '/api/workflowTemplate')
SERVER_DOWN_CODES = (-1, 301, 307)


class QueueMock(Queue.Queue):
    def __init__(self, completion_handler):
        self.completion_handler = completion_handler
        super(QueueMock, self).__init__()

    def put_nowait(self, oper):
        self.completion_handler(oper)


def _recover_function_mock(action, resource, data, headers, binary=False):
    pass


def rest_call_function_mock(action, resource, data, headers, binary=False):
    if rest_call_function_mock.RESPOND_WITH_ERROR:
        return 400, 'error_status', 'error_description', None
    if rest_call_function_mock.RESPOND_WITH_SERVER_DOWN in SERVER_DOWN_CODES:
        val = rest_call_function_mock.RESPOND_WITH_SERVER_DOWN
        return val, 'error_status', 'error_description', None
    if action == 'GET':
        return _get_handler(resource)
    elif action == 'DELETE':
        return _delete_handler(resource)
    elif action == 'POST':
        return _post_handler(resource, binary)
    else:
        return 0, None, None, None


def _get_handler(resource):
    if resource == GET_200[2]:
        if rest_call_function_mock.TEMPLATES_MISSING:
            data = jsonutils.loads('[]')
        else:
            data = jsonutils.loads(
                '[{"name":"openstack_l2_l3"},{"name":"openstack_l4"}]'
            )
        return 200, '', '', data

    if resource in GET_200:
        return 200, '', '', ''
    else:
        data = jsonutils.loads('{"complete":"True", "success": "True"}')
        return 202, '', '', data


def _delete_handler(resource):
    return 404, '', '', {'message': 'Not Found'}


def _post_handler(resource, binary):
    if re.search(r'/api/workflow/.+/action/.+', resource):
        data = jsonutils.loads('{"uri":"some_uri"}')
        return 202, '', '', data
    elif re.search(r'/api/service\?name=.+', resource):
        data = jsonutils.loads('{"links":{"actions":{"provision":"someuri"}}}')
        return 201, '', '', data
    elif binary:
        return 201, '', '', ''
    else:
        return 202, '', '', ''

RADWARE_PROVIDER = ('LOADBALANCER:radware:neutron_lbaas.services.'
                    'loadbalancer.drivers.radware.driver.'
                    'LoadBalancerDriver:default')


class TestLoadBalancerPluginBase(
    test_db_loadbalancer.LoadBalancerPluginDbTestCase):

    def setUp(self):
        super(TestLoadBalancerPluginBase, self).setUp(
            lbaas_provider=RADWARE_PROVIDER)

        loaded_plugins = manager.NeutronManager().get_service_plugins()
        self.plugin_instance = loaded_plugins[constants.LOADBALANCER]


class TestLoadBalancerPlugin(TestLoadBalancerPluginBase):
    def setUp(self):
        super(TestLoadBalancerPlugin, self).setUp()

        rest_call_function_mock.__dict__.update(
            {'RESPOND_WITH_ERROR': False})
        rest_call_function_mock.__dict__.update(
            {'TEMPLATES_MISSING': False})
        rest_call_function_mock.__dict__.update(
            {'RESPOND_WITH_SERVER_DOWN': 200})

        self.operation_completer_start_mock = mock.Mock(
            return_value=None)
        self.operation_completer_join_mock = mock.Mock(
            return_value=None)
        self.driver_rest_call_mock = mock.Mock(
            side_effect=rest_call_function_mock)
        self.flip_servers_mock = mock.Mock(
            return_value=None)
        self.recover_mock = mock.Mock(
            side_effect=_recover_function_mock)

        radware_driver = self.plugin_instance.drivers['radware']
        radware_driver.completion_handler.start = (
            self.operation_completer_start_mock)
        radware_driver.completion_handler.join = (
            self.operation_completer_join_mock)
        self.orig_call = radware_driver.rest_client.call
        self.orig__call = radware_driver.rest_client._call
        radware_driver.rest_client.call = self.driver_rest_call_mock
        radware_driver.rest_client._call = self.driver_rest_call_mock
        radware_driver.rest_client._flip_servers = self.flip_servers_mock
        radware_driver.rest_client._recover = self.recover_mock
        radware_driver.completion_handler.rest_client.call = (
            self.driver_rest_call_mock)

        radware_driver.queue = QueueMock(
            radware_driver.completion_handler.handle_operation_completion)

        self.addCleanup(radware_driver.completion_handler.join)

    def test_get_pip(self):
        """Call _get_pip twice and verify that a Port is created once."""
        port_dict = {'fixed_ips': [{'subnet_id': '10.10.10.10',
                                    'ip_address': '11.11.11.11'}]}
        port_data = {
            'tenant_id': 'tenant_id',
            'name': 'port_name',
            'network_id': 'network_id',
            'mac_address': attributes.ATTR_NOT_SPECIFIED,
            'admin_state_up': False,
            'device_id': '',
            'device_owner': 'neutron:' + constants.LOADBALANCER,
            'fixed_ips': [{'subnet_id': '10.10.10.10'}]
        }
        self.plugin_instance._core_plugin.get_ports = mock.Mock(
            return_value=[])
        self.plugin_instance._core_plugin.create_port = mock.Mock(
            return_value=port_dict)
        radware_driver = self.plugin_instance.drivers['radware']
        radware_driver._get_pip(context.get_admin_context(),
                                'tenant_id', 'port_name',
                                'network_id', '10.10.10.10')
        self.plugin_instance._core_plugin.get_ports.assert_called_once_with(
                mock.ANY, filters={'name': ['port_name']})
        self.plugin_instance._core_plugin.create_port.assert_called_once_with(
                mock.ANY, {'port': port_data})
        self.plugin_instance._core_plugin.create_port.reset_mock()
        self.plugin_instance._core_plugin.get_ports.reset_mock()
        self.plugin_instance._core_plugin.get_ports.return_value = [port_dict]
        radware_driver._get_pip(context.get_admin_context(),
                                'tenant_id', 'port_name',
                                'network_id', '10.10.10.10')
        self.plugin_instance._core_plugin.get_ports.assert_called_once_with(
                mock.ANY, filters={'name': ['port_name']})
        self.assertFalse(self.plugin_instance._core_plugin.create_port.called)

    def test_rest_client_recover_was_called(self):
        """Call the real REST client and verify _recover is called."""
        radware_driver = self.plugin_instance.drivers['radware']
        radware_driver.rest_client.call = self.orig_call
        radware_driver.rest_client._call = self.orig__call
        self.assertRaises(r_exc.RESTRequestFailure,
                          radware_driver._verify_workflow_templates)
        self.recover_mock.assert_called_once_with('GET',
                                                  '/api/workflowTemplate',
                                                  None, None, False)

    def test_rest_client_flip_servers(self):
        radware_driver = self.plugin_instance.drivers['radware']
        server = radware_driver.rest_client.server
        sec_server = radware_driver.rest_client.secondary_server
        radware_driver.rest_client._flip_servers()
        self.assertEqual(server,
                         radware_driver.rest_client.secondary_server)
        self.assertEqual(sec_server,
                         radware_driver.rest_client.server)

    def test_verify_workflow_templates_server_down(self):
        """Test the rest call failure when backend is down."""
        for value in SERVER_DOWN_CODES:
            rest_call_function_mock.__dict__.update(
                {'RESPOND_WITH_SERVER_DOWN': value})
            self.assertRaises(r_exc.RESTRequestFailure,
                              self.plugin_instance.drivers['radware'].
                              _verify_workflow_templates)

    def test_verify_workflow_templates(self):
        """Test the rest call failure handling by Exception raising."""
        rest_call_function_mock.__dict__.update(
            {'TEMPLATES_MISSING': True})

        self.assertRaises(r_exc.WorkflowMissing,
                          self.plugin_instance.drivers['radware'].
                          _verify_workflow_templates)

    def test_create_vip_failure(self):
        """Test the rest call failure handling by Exception raising."""
        with self.network() as network:
            with self.subnet(network=network) as subnet:
                with self.pool(do_delete=False,
                               provider='radware',
                               subnet_id=subnet['subnet']['id']) as pool:
                    vip_data = {
                        'name': 'vip1',
                        'subnet_id': subnet['subnet']['id'],
                        'pool_id': pool['pool']['id'],
                        'description': '',
                        'protocol_port': 80,
                        'protocol': 'HTTP',
                        'connection_limit': -1,
                        'admin_state_up': True,
                        'status': constants.PENDING_CREATE,
                        'tenant_id': self._tenant_id,
                        'session_persistence': ''
                    }

                    rest_call_function_mock.__dict__.update(
                        {'RESPOND_WITH_ERROR': True})

                    self.assertRaises(r_exc.RESTRequestFailure,
                                      self.plugin_instance.create_vip,
                                      context.get_admin_context(),
                                      {'vip': vip_data})

    def test_create_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           subnet_id=subnet['subnet']['id']) as pool:
                vip_data = {
                    'name': 'vip1',
                    'subnet_id': subnet['subnet']['id'],
                    'pool_id': pool['pool']['id'],
                    'description': '',
                    'protocol_port': 80,
                    'protocol': 'HTTP',
                    'connection_limit': -1,
                    'admin_state_up': True,
                    'status': constants.PENDING_CREATE,
                    'tenant_id': self._tenant_id,
                    'session_persistence': ''
                }

                vip = self.plugin_instance.create_vip(
                    context.get_admin_context(), {'vip': vip_data})

                # Test creation REST calls
                calls = [
                    mock.call('GET', u'/api/service/srv_' +
                              subnet['subnet']['network_id'], None, None),
                    mock.call('POST', u'/api/service?name=srv_' +
                              subnet['subnet']['network_id'] + '&tenant=' +
                              vip['tenant_id'], mock.ANY,
                              driver.CREATE_SERVICE_HEADER),
                    mock.call('GET', u'/api/workflow/l2_l3_' +
                              subnet['subnet']['network_id'], None, None),
                    mock.call('POST', '/api/workflow/l2_l3_' +
                              subnet['subnet']['network_id'] +
                              '/action/setup_l2_l3',
                              mock.ANY, driver.TEMPLATE_HEADER),
                    mock.call('POST', 'someuri',
                              None, driver.PROVISION_HEADER),


                    mock.call('POST', '/api/workflowTemplate/' +
                              'openstack_l4' +
                              '?name=' + pool['pool']['id'],
                              mock.ANY,
                              driver.TEMPLATE_HEADER),
                    mock.call('POST', '/api/workflowTemplate/' +
                              'openstack_l2_l3' +
                              '?name=l2_l3_' + subnet['subnet']['network_id'],
                              mock.ANY,
                              driver.TEMPLATE_HEADER),

                    mock.call('POST', '/api/workflow/' + pool['pool']['id'] +
                              '/action/BaseCreate',
                              mock.ANY, driver.TEMPLATE_HEADER),
                    mock.call('GET', '/api/workflow/' +
                              pool['pool']['id'], None, None)
                ]
                self.driver_rest_call_mock.assert_has_calls(calls,
                                                            any_order=True)

                #Test DB
                new_vip = self.plugin_instance.get_vip(
                    context.get_admin_context(),
                    vip['id']
                )
                self.assertEqual(constants.ACTIVE, new_vip['status'])

                # Delete VIP
                self.plugin_instance.delete_vip(
                    context.get_admin_context(), vip['id'])

                # Test deletion REST calls
                calls = [
                    mock.call('DELETE', u'/api/workflow/' + pool['pool']['id'],
                              None, None)
                ]
                self.driver_rest_call_mock.assert_has_calls(
                    calls, any_order=True)

    def test_create_vip_2_leg(self):
        """Test creation of a VIP where Alteon VIP and PIP are different."""

        with self.subnet(cidr='10.0.0.0/24') as subnet:
            with self.subnet(cidr='10.0.1.0/24') as pool_sub:
                with self.pool(provider='radware',
                               subnet_id=pool_sub['subnet']['id']) as pool:
                    vip_data = {
                        'name': 'vip1',
                        'subnet_id': subnet['subnet']['id'],
                        'pool_id': pool['pool']['id'],
                        'description': '',
                        'protocol_port': 80,
                        'protocol': 'HTTP',
                        'connection_limit': -1,
                        'admin_state_up': True,
                        'status': constants.PENDING_CREATE,
                        'tenant_id': self._tenant_id,
                        'session_persistence': ''
                    }

                    vip = self.plugin_instance.create_vip(
                        context.get_admin_context(), {'vip': vip_data})
                    name_suffix = '%s_%s' % (subnet['subnet']['network_id'],
                                             pool_sub['subnet']['network_id'])
                    # Test creation REST calls
                    calls = [
                        mock.call('GET', '/api/workflowTemplate', None, None),
                        mock.call('GET', '/api/service/srv_' + name_suffix,
                                  None, None),
                        mock.call('POST', '/api/service?name=srv_' +
                                  name_suffix + '&tenant=' + vip['tenant_id'],
                                  mock.ANY, driver.CREATE_SERVICE_HEADER),
                        mock.call('POST', 'someuri',
                                  None, driver.PROVISION_HEADER),
                        mock.call('GET', '/api/workflow/l2_l3_' + name_suffix,
                                  None, None),
                        mock.call('POST', '/api/workflowTemplate/' +
                                  'openstack_l2_l3' +
                                  '?name=l2_l3_' + name_suffix,
                                  mock.ANY,
                                  driver.TEMPLATE_HEADER),
                        mock.call('POST', '/api/workflow/l2_l3_' +
                                  name_suffix + '/action/setup_l2_l3',
                                  mock.ANY, driver.TEMPLATE_HEADER),
                        mock.call('GET', '/api/workflow/' +
                                  pool['pool']['id'], None, None),
                        mock.call('POST', '/api/workflowTemplate/' +
                                  'openstack_l4' +
                                  '?name=' + pool['pool']['id'],
                                  mock.ANY,
                                  driver.TEMPLATE_HEADER),
                        mock.call('POST', '/api/workflow/' +
                                  pool['pool']['id'] + '/action/BaseCreate',
                                  mock.ANY, driver.TEMPLATE_HEADER)
                    ]
                    self.driver_rest_call_mock.assert_has_calls(calls)
                    #Test DB
                    new_vip = self.plugin_instance.get_vip(
                        context.get_admin_context(),
                        vip['id']
                    )
                    self.assertEqual(constants.ACTIVE, new_vip['status'])

                    # Test that PIP neutron port was created
                    pip_port_filter = {
                        'name': ['pip_' + vip['id']],
                    }
                    plugin = manager.NeutronManager.get_plugin()
                    num_ports = plugin.get_ports_count(
                        context.get_admin_context(), filters=pip_port_filter)
                    self.assertTrue(num_ports > 0)

                    # Delete VIP
                    self.plugin_instance.delete_vip(
                        context.get_admin_context(), vip['id'])

                    # Test deletion REST calls
                    calls = [
                        mock.call('DELETE', u'/api/workflow/' +
                                  pool['pool']['id'], None, None)
                    ]
                    self.driver_rest_call_mock.assert_has_calls(calls)

    def test_update_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           do_delete=False,
                           subnet_id=subnet['subnet']['id']) as pool:
                vip_data = {
                    'name': 'vip1',
                    'subnet_id': subnet['subnet']['id'],
                    'pool_id': pool['pool']['id'],
                    'description': '',
                    'protocol_port': 80,
                    'protocol': 'HTTP',
                    'connection_limit': -1,
                    'admin_state_up': True,
                    'status': constants.PENDING_CREATE,
                    'tenant_id': self._tenant_id,
                    'session_persistence': ''
                }

                vip = self.plugin_instance.create_vip(
                    context.get_admin_context(), {'vip': vip_data})

                vip_data['status'] = constants.PENDING_UPDATE
                self.plugin_instance.update_vip(
                    context.get_admin_context(),
                    vip['id'], {'vip': vip_data})

                # Test REST calls
                calls = [
                    mock.call('POST', '/api/workflow/' + pool['pool']['id'] +
                              '/action/BaseCreate',
                              mock.ANY, driver.TEMPLATE_HEADER),
                ]
                self.driver_rest_call_mock.assert_has_calls(
                    calls, any_order=True)

                updated_vip = self.plugin_instance.get_vip(
                    context.get_admin_context(), vip['id'])
                self.assertEqual(constants.ACTIVE, updated_vip['status'])

                # delete VIP
                self.plugin_instance.delete_vip(
                    context.get_admin_context(), vip['id'])

    def test_update_vip_2_leg(self):
        """Test update of a VIP where Alteon VIP and PIP are different."""

        with self.subnet(cidr='10.0.0.0/24') as subnet:
            with self.subnet(cidr='10.0.1.0/24') as pool_subnet:
                with self.pool(provider='radware',
                               subnet_id=pool_subnet['subnet']['id']) as pool:
                    vip_data = {
                        'name': 'vip1',
                        'subnet_id': subnet['subnet']['id'],
                        'pool_id': pool['pool']['id'],
                        'description': '',
                        'protocol_port': 80,
                        'protocol': 'HTTP',
                        'connection_limit': -1,
                        'admin_state_up': True,
                        'status': constants.PENDING_CREATE,
                        'tenant_id': self._tenant_id,
                        'session_persistence': ''
                    }

                    vip = self.plugin_instance.create_vip(
                        context.get_admin_context(), {'vip': vip_data})

                    self.plugin_instance.update_vip(
                        context.get_admin_context(),
                        vip['id'], {'vip': vip_data})

                    # Test REST calls
                    calls = [
                        mock.call('POST', '/api/workflow/' +
                                  pool['pool']['id'] + '/action/BaseCreate',
                                  mock.ANY, driver.TEMPLATE_HEADER),
                    ]
                    self.driver_rest_call_mock.assert_has_calls(calls)

                    updated_vip = self.plugin_instance.get_vip(
                        context.get_admin_context(), vip['id'])
                    self.assertEqual(constants.ACTIVE, updated_vip['status'])

                    # delete VIP
                    self.plugin_instance.delete_vip(
                        context.get_admin_context(), vip['id'])

    def test_delete_vip_failure(self):
        plugin = self.plugin_instance

        with self.network() as network:
            with self.subnet(network=network) as subnet:
                with self.pool(do_delete=False,
                               provider='radware',
                               subnet_id=subnet['subnet']['id']) as pool:
                    with contextlib.nested(
                        self.member(pool_id=pool['pool']['id'],
                                    do_delete=False),
                        self.member(pool_id=pool['pool']['id'],
                                    address='192.168.1.101',
                                    do_delete=False),
                        self.health_monitor(do_delete=False),
                        self.vip(pool=pool, subnet=subnet, do_delete=False)
                    ) as (mem1, mem2, hm, vip):

                        plugin.create_pool_health_monitor(
                            context.get_admin_context(), hm, pool['pool']['id']
                        )

                        rest_call_function_mock.__dict__.update(
                            {'RESPOND_WITH_ERROR': True})

                        plugin.delete_vip(
                            context.get_admin_context(), vip['vip']['id'])

                        u_vip = plugin.get_vip(
                            context.get_admin_context(), vip['vip']['id'])
                        u_pool = plugin.get_pool(
                            context.get_admin_context(), pool['pool']['id'])
                        u_mem1 = plugin.get_member(
                            context.get_admin_context(), mem1['member']['id'])
                        u_mem2 = plugin.get_member(
                            context.get_admin_context(), mem2['member']['id'])
                        u_phm = plugin.get_pool_health_monitor(
                            context.get_admin_context(),
                            hm['health_monitor']['id'], pool['pool']['id'])

                        self.assertEqual(constants.ERROR, u_vip['status'])
                        self.assertEqual(constants.ACTIVE, u_pool['status'])
                        self.assertEqual(constants.ACTIVE, u_mem1['status'])
                        self.assertEqual(constants.ACTIVE, u_mem2['status'])
                        self.assertEqual(constants.ACTIVE, u_phm['status'])

    def test_delete_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           do_delete=False,
                           subnet_id=subnet['subnet']['id']) as pool:
                vip_data = {
                    'name': 'vip1',
                    'subnet_id': subnet['subnet']['id'],
                    'pool_id': pool['pool']['id'],
                    'description': '',
                    'protocol_port': 80,
                    'protocol': 'HTTP',
                    'connection_limit': -1,
                    'admin_state_up': True,
                    'status': constants.PENDING_CREATE,
                    'tenant_id': self._tenant_id,
                    'session_persistence': ''
                }

                vip = self.plugin_instance.create_vip(
                    context.get_admin_context(), {'vip': vip_data})

                self.plugin_instance.delete_vip(
                    context.get_admin_context(), vip['id'])

                calls = [
                    mock.call('DELETE', '/api/workflow/' + pool['pool']['id'],
                              None, None)
                ]
                self.driver_rest_call_mock.assert_has_calls(
                    calls, any_order=True)

                self.assertRaises(loadbalancer.VipNotFound,
                                  self.plugin_instance.get_vip,
                                  context.get_admin_context(), vip['id'])

    def test_delete_vip_2_leg(self):
        """Test deletion of a VIP where Alteon VIP and PIP are different."""

        self.driver_rest_call_mock.reset_mock()
        with self.subnet(cidr='10.0.0.0/24') as subnet:
            with self.subnet(cidr='10.0.1.0/24') as pool_subnet:
                with self.pool(provider='radware',
                               do_delete=False,
                               subnet_id=pool_subnet['subnet']['id']) as pool:
                    vip_data = {
                        'name': 'vip1',
                        'subnet_id': subnet['subnet']['id'],
                        'pool_id': pool['pool']['id'],
                        'description': '',
                        'protocol_port': 80,
                        'protocol': 'HTTP',
                        'connection_limit': -1,
                        'admin_state_up': True,
                        'status': constants.PENDING_CREATE,
                        'tenant_id': self._tenant_id,
                        'session_persistence': ''
                    }

                    vip = self.plugin_instance.create_vip(
                        context.get_admin_context(), {'vip': vip_data})

                    self.plugin_instance.delete_vip(
                        context.get_admin_context(), vip['id'])

                    calls = [
                        mock.call('DELETE', '/api/workflow/' +
                                  pool['pool']['id'], None, None)
                    ]
                    self.driver_rest_call_mock.assert_has_calls(calls)

                    # Test that PIP neutron port was deleted
                    pip_port_filter = {
                        'name': ['pip_' + vip['id']],
                    }
                    plugin = manager.NeutronManager.get_plugin()
                    num_ports = plugin.get_ports_count(
                        context.get_admin_context(), filters=pip_port_filter)
                    self.assertTrue(num_ports == 0)

                    self.assertRaises(loadbalancer.VipNotFound,
                                      self.plugin_instance.get_vip,
                                      context.get_admin_context(), vip['id'])

    def test_update_pool(self):
        with self.subnet():
            with self.pool() as pool:
                del pool['pool']['provider']
                del pool['pool']['status']
                self.plugin_instance.update_pool(
                    context.get_admin_context(),
                    pool['pool']['id'], pool)
                pool_db = self.plugin_instance.get_pool(
                    context.get_admin_context(), pool['pool']['id'])
                self.assertEqual(constants.PENDING_UPDATE, pool_db['status'])

    def test_delete_pool_with_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           do_delete=False,
                           subnet_id=subnet['subnet']['id']) as pool:
                with self.vip(pool=pool, subnet=subnet):
                    self.assertRaises(loadbalancer.PoolInUse,
                                      self.plugin_instance.delete_pool,
                                      context.get_admin_context(),
                                      pool['pool']['id'])

    def test_create_member_with_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           subnet_id=subnet['subnet']['id']) as p:
                with self.vip(pool=p, subnet=subnet):
                    with self.member(pool_id=p['pool']['id']):
                        calls = [
                            mock.call(
                                'POST', '/api/workflow/' + p['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            ),
                            mock.call(
                                'POST', '/api/workflow/' + p['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            )
                        ]
                        self.driver_rest_call_mock.assert_has_calls(
                            calls, any_order=True)

    def test_create_member_on_different_subnets(self):
        with contextlib.nested(
            self.subnet(),
            self.subnet(cidr='20.0.0.0/24'),
            self.subnet(cidr='30.0.0.0/24')
        ) as (vip_sub, pool_sub, member_sub):
            with self.pool(provider='radware',
                           subnet_id=pool_sub['subnet']['id']) as pool:
                with contextlib.nested(
                    self.port(subnet=vip_sub,
                              fixed_ips=[{'ip_address': '10.0.0.2'}]),
                    self.port(subnet=pool_sub,
                              fixed_ips=[{'ip_address': '20.0.0.2'}]),
                    self.port(subnet=member_sub,
                              fixed_ips=[{'ip_address': '30.0.0.2'}])
                ):
                    with contextlib.nested(
                        self.member(pool_id=pool['pool']['id'],
                                    address='10.0.0.2'),
                        self.member(pool_id=pool['pool']['id'],
                                    address='20.0.0.2'),
                        self.member(pool_id=pool['pool']['id'],
                                    address='30.0.0.2')
                    ) as (member_vip, member_pool, member_out):
                        with self.vip(pool=pool, subnet=vip_sub):
                            calls = [
                                mock.call(
                                    'POST', '/api/workflow/' +
                                    pool['pool']['id'] +
                                    '/action/BaseCreate',
                                    mock.ANY, driver.TEMPLATE_HEADER
                                )
                            ]
                            self.driver_rest_call_mock.assert_has_calls(
                                calls, any_order=True)

                            mock_calls = self.driver_rest_call_mock.mock_calls
                            params = mock_calls[-2][1][2]['parameters']
                            member_subnet_array = params['member_subnet_array']
                            member_mask_array = params['member_mask_array']
                            member_gw_array = params['member_gw_array']
                            self.assertEqual(['10.0.0.0',
                                              '255.255.255.255',
                                              '30.0.0.0'],
                                             member_subnet_array)
                            self.assertEqual(['255.255.255.0',
                                              '255.255.255.255',
                                              '255.255.255.0'],
                                             member_mask_array)
                            self.assertEqual(
                                [pool_sub['subnet']['gateway_ip'],
                                 '255.255.255.255',
                                 pool_sub['subnet']['gateway_ip']],
                                member_gw_array)

    def test_create_member_on_different_subnet_no_port(self):
        with contextlib.nested(
            self.subnet(),
            self.subnet(cidr='20.0.0.0/24'),
            self.subnet(cidr='30.0.0.0/24')
        ) as (vip_sub, pool_sub, member_sub):
            with self.pool(provider='radware',
                           subnet_id=pool_sub['subnet']['id']) as pool:
                with self.member(pool_id=pool['pool']['id'],
                                 address='30.0.0.2'):
                    with self.vip(pool=pool, subnet=vip_sub):
                        calls = [
                            mock.call(
                                'POST', '/api/workflow/' +
                                pool['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            )
                        ]
                        self.driver_rest_call_mock.assert_has_calls(
                            calls, any_order=True)

                        mock_calls = self.driver_rest_call_mock.mock_calls
                        params = mock_calls[-2][1][2]['parameters']
                        member_subnet_array = params['member_subnet_array']
                        member_mask_array = params['member_mask_array']
                        member_gw_array = params['member_gw_array']
                        self.assertEqual(['30.0.0.2'],
                                         member_subnet_array)
                        self.assertEqual(['255.255.255.255'],
                                         member_mask_array)
                        self.assertEqual([pool_sub['subnet']['gateway_ip']],
                                         member_gw_array)

    def test_create_member_on_different_subnet_multiple_ports(self):
        cfg.CONF.set_override("allow_overlapping_ips", 'true')
        with self.network() as other_net:
            with contextlib.nested(
                self.subnet(),
                self.subnet(cidr='20.0.0.0/24'),
                self.subnet(cidr='30.0.0.0/24'),
                self.subnet(network=other_net, cidr='30.0.0.0/24')
            ) as (vip_sub, pool_sub, member_sub1, member_sub2):
                with self.pool(provider='radware',
                               subnet_id=pool_sub['subnet']['id']) as pool:
                    with contextlib.nested(
                        self.port(subnet=member_sub1,
                                  fixed_ips=[{'ip_address': '30.0.0.2'}]),
                        self.port(subnet=member_sub2,
                                  fixed_ips=[{'ip_address': '30.0.0.2'}])):
                        with self.member(pool_id=pool['pool']['id'],
                                         address='30.0.0.2'):
                            with self.vip(pool=pool, subnet=vip_sub):
                                calls = [
                                    mock.call(
                                        'POST', '/api/workflow/' +
                                        pool['pool']['id'] +
                                        '/action/BaseCreate',
                                        mock.ANY, driver.TEMPLATE_HEADER
                                    )
                                ]
                                self.driver_rest_call_mock.assert_has_calls(
                                    calls, any_order=True)

                                calls = self.driver_rest_call_mock.mock_calls
                                params = calls[-2][1][2]['parameters']
                                m_sub_array = params['member_subnet_array']
                                m_mask_array = params['member_mask_array']
                                m_gw_array = params['member_gw_array']
                                self.assertEqual(['30.0.0.2'],
                                                 m_sub_array)
                                self.assertEqual(['255.255.255.255'],
                                                 m_mask_array)
                                self.assertEqual(
                                    [pool_sub['subnet']['gateway_ip']],
                                    m_gw_array)

    def test_update_member_with_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           subnet_id=subnet['subnet']['id']) as p:
                with self.member(pool_id=p['pool']['id']) as member:
                    with self.vip(pool=p, subnet=subnet):
                        self.plugin_instance.update_member(
                            context.get_admin_context(),
                            member['member']['id'], member
                        )
                        calls = [
                            mock.call(
                                'POST', '/api/workflow/' + p['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            ),
                            mock.call(
                                'POST', '/api/workflow/' + p['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            )
                        ]
                        self.driver_rest_call_mock.assert_has_calls(
                            calls, any_order=True)

                        updated_member = self.plugin_instance.get_member(
                            context.get_admin_context(),
                            member['member']['id']
                        )

                        updated_member = self.plugin_instance.get_member(
                            context.get_admin_context(),
                            member['member']['id']
                        )
                        self.assertEqual(constants.ACTIVE,
                                         updated_member['status'])

    def test_update_member_without_vip(self):
        with self.subnet():
            with self.pool(provider='radware') as pool:
                with self.member(pool_id=pool['pool']['id']) as member:
                    member['member']['status'] = constants.PENDING_UPDATE
                    updated_member = self.plugin_instance.update_member(
                        context.get_admin_context(),
                        member['member']['id'], member
                    )
                    self.assertEqual(constants.PENDING_UPDATE,
                                     updated_member['status'])

    def test_delete_member_with_vip(self):
        with self.subnet() as subnet:
            with self.pool(provider='radware',
                           subnet_id=subnet['subnet']['id']) as p:
                with self.member(pool_id=p['pool']['id'],
                                 do_delete=False) as m:
                    with self.vip(pool=p, subnet=subnet):

                        # Reset mock and
                        # wait for being sure the member
                        # Changed status from PENDING-CREATE
                        # to ACTIVE

                        self.plugin_instance.delete_member(
                            context.get_admin_context(),
                            m['member']['id']
                        )

                        name, args, kwargs = (
                            self.driver_rest_call_mock.mock_calls[-2]
                        )
                        deletion_post_graph = str(args[2])

                        self.assertTrue(re.search(
                            r'.*\'member_address_array\': \[\].*',
                            deletion_post_graph
                        ))

                        calls = [
                            mock.call(
                                'POST', '/api/workflow/' + p['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            )
                        ]
                        self.driver_rest_call_mock.assert_has_calls(
                            calls, any_order=True)

                        self.assertRaises(loadbalancer.MemberNotFound,
                                          self.plugin_instance.get_member,
                                          context.get_admin_context(),
                                          m['member']['id'])

    def test_delete_member_without_vip(self):
        with self.subnet():
            with self.pool(provider='radware') as p:
                with self.member(pool_id=p['pool']['id'],
                                 do_delete=False) as m:
                    self.plugin_instance.delete_member(
                        context.get_admin_context(), m['member']['id']
                    )
                    self.assertRaises(loadbalancer.MemberNotFound,
                                      self.plugin_instance.get_member,
                                      context.get_admin_context(),
                                      m['member']['id'])

    def test_create_hm_with_vip(self):
        with self.subnet() as subnet:
            with self.health_monitor() as hm:
                with self.pool(provider='radware',
                               subnet_id=subnet['subnet']['id']) as pool:
                    with self.vip(pool=pool, subnet=subnet):

                        self.plugin_instance.create_pool_health_monitor(
                            context.get_admin_context(),
                            hm, pool['pool']['id']
                        )

                        # Test REST calls
                        calls = [
                            mock.call(
                                'POST', '/api/workflow/' + pool['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            ),
                            mock.call(
                                'POST', '/api/workflow/' + pool['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            )
                        ]
                        self.driver_rest_call_mock.assert_has_calls(
                            calls, any_order=True)

                        phm = self.plugin_instance.get_pool_health_monitor(
                            context.get_admin_context(),
                            hm['health_monitor']['id'], pool['pool']['id']
                        )
                        self.assertEqual(constants.ACTIVE, phm['status'])

    def test_delete_pool_hm_with_vip(self):
        with self.subnet() as subnet:
            with self.health_monitor(do_delete=False) as hm:
                with self.pool(provider='radware',
                               subnet_id=subnet['subnet']['id']) as pool:
                    with self.vip(pool=pool, subnet=subnet):
                        self.plugin_instance.create_pool_health_monitor(
                            context.get_admin_context(),
                            hm, pool['pool']['id']
                        )

                        self.plugin_instance.delete_pool_health_monitor(
                            context.get_admin_context(),
                            hm['health_monitor']['id'],
                            pool['pool']['id']
                        )

                        name, args, kwargs = (
                            self.driver_rest_call_mock.mock_calls[-2]
                        )
                        deletion_post_graph = str(args[2])

                        self.assertTrue(re.search(
                            r'.*\'hm_uuid_array\': \[\].*',
                            deletion_post_graph
                        ))

                        calls = [
                            mock.call(
                                'POST', '/api/workflow/' + pool['pool']['id'] +
                                '/action/BaseCreate',
                                mock.ANY, driver.TEMPLATE_HEADER
                            )
                        ]
                        self.driver_rest_call_mock.assert_has_calls(
                            calls, any_order=True)

                        self.assertRaises(
                            loadbalancer.PoolMonitorAssociationNotFound,
                            self.plugin_instance.get_pool_health_monitor,
                            context.get_admin_context(),
                            hm['health_monitor']['id'],
                            pool['pool']['id']
                        )
