# Copyright 2019 Red Hat, Inc.
# All Rights Reserved.
#
#    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 neutron.agent.linux import of_monitor
from neutron.common import utils
from neutron.tests.common import net_helpers
from neutron.tests.functional import base as functional_base


class OFMonitorTestCase(functional_base.BaseSudoTestCase):

    DEFAULT_FLOW = {'table': 0, 'cookie': '0', 'actions': 'NORMAL'}

    def setUp(self):
        super().setUp()
        self.bridge = self.useFixture(net_helpers.OVSBridgeFixture()).bridge
        self.of_monitor = of_monitor.OFMonitor(self.bridge.br_name,
                                               start=False)
        self.addCleanup(self.of_monitor.stop)

    def _format_flow(self, flow, event_type):
        deleted = ''
        if event_type == 'DELETED':
            deleted = 'reason=delete'
        table = 'table=%s' % flow['table']
        cookie = flow.get('cookie') or hex(self.bridge._default_cookie)
        # NOTE(ralonsoh): remove PY2 "L" suffix in longs
        cookie = 'cookie=' + cookie.rstrip('L')
        filters = []
        if flow.get('in_port'):
            filters.append('in_port=%s' % flow.get('in_port'))
        if flow.get('dl_vlan'):
            filters.append('dl_vlan=%s' % flow.get('dl_vlan'))
        if flow.get('dl_src'):
            filters.append('dl_src=%s' % flow.get('dl_src'))
        filters = ','.join(filters)
        actions = ''
        if flow.get('actions'):
            actions += 'actions=%s' % flow.get('actions')
        flow_sections = [section for section
                         in (deleted, table, cookie, filters, actions)
                         if section]
        return ' '.join(flow_sections)

    def _check_flow(self, reference_flow, event_type):
        def _read_and_check():
            event = self.of_monitor.of_events
            if len(event) == 1:
                events_container.append(event[0])
                return True
            return False

        events_container = []
        try:
            utils.wait_until_true(_read_and_check, timeout=5)
        except utils.WaitTimeout:
            self.fail('Flow "{}" with action {} not found'.format(
                reference_flow, event_type))
        event = events_container.pop()
        self.assertEqual(event_type, event.event_type)
        self.assertEqual(self._format_flow(reference_flow, event_type),
                         event.flow)

    def test_of_events(self):
        self.of_monitor.start()
        self._check_flow(self.DEFAULT_FLOW, 'ADDED')

        flow = {'table': 10, 'in_port': 20, 'dl_vlan': 30,
                'dl_src': '00:00:00:00:00:01', 'actions': 'NORMAL'}
        self.bridge.add_flow(**flow)
        self._check_flow(flow, 'ADDED')

        flow['table'] = 50
        self.bridge.add_flow(**flow)
        self._check_flow(flow, 'ADDED')

        flow['actions'] = 'resubmit:100'
        self.bridge.mod_flow(**flow)
        self._check_flow(flow, 'MODIFIED')

        flow['table'] = 10
        flow['actions'] = 'NORMAL'
        flow_to_delete = flow.copy()
        flow_to_delete.pop('actions')
        self.bridge.delete_flows(**flow_to_delete)
        self._check_flow(flow, 'DELETED')
