# Copyright 2016 Hewlett Packard Enterprise Development LP
#
# 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 abc

from neutron_lib import rpc as n_rpc
from oslo_log import helpers as log_helpers
import oslo_messaging

from neutron.api.rpc.callbacks.consumer import registry
from neutron.api.rpc.callbacks import resources
from neutron.api.rpc.handlers import resources_rpc
from neutron.services.trunk.rpc import constants as trunk_consts

# This module contains stub (client-side) and skeleton (server-side)
# proxy code that executes in the Neutron L2 Agent process space. This
# is needed if trunk service plugin drivers have a remote component
# (e.g. agent), that needs to communicate with the Neutron Server.

# The Agent side exposes the following remote methods:
#
# - update methods to learn about a trunk and its subports: these
#   methods are used by the server to tell the agent about trunk
#   updates; agents may selectively choose to listen to either
#   trunk or subports updates or both.
#
# For server-side stub and skeleton proxy code, please look at server.py


class TrunkSkeleton(object):
    """Skeleton proxy code for server->agent communication."""

    def __init__(self):
        registry.register(self.handle_trunks, resources.TRUNK)
        registry.register(self.handle_subports, resources.SUBPORT)

        self._connection = n_rpc.Connection()
        endpoints = [resources_rpc.ResourcesPushRpcCallback()]
        topic = resources_rpc.resource_type_versioned_topic(resources.SUBPORT)
        self._connection.create_consumer(topic, endpoints, fanout=True)
        topic = resources_rpc.resource_type_versioned_topic(resources.TRUNK)
        self._connection.create_consumer(topic, endpoints, fanout=True)
        self._connection.consume_in_threads()

    def unregister(self):
        self._connection.close()

    @abc.abstractmethod
    def handle_trunks(self, context, resource_type, trunks, event_type):
        """Handle trunk events."""
        # if common logic may be extracted out, consider making a base
        # version of this method that can be overridden by the inherited
        # skeleton.
        # NOTE: If trunk is not managed by the agent, the notification can
        # either be ignored or cached for future use.

    @abc.abstractmethod
    def handle_subports(self, context, resource_type, subports, event_type):
        """Handle subports event."""
        # if common logic may be extracted out, consider making a base
        # version of this method that can be overridden by the inherited
        # skeleton.
        # NOTE: If the subport belongs to a trunk which the agent does not
        # manage, the notification should be ignored.


class TrunkStub(object):
    """Stub proxy code for agent->server communication."""
    # API HISTORY
    #   1.0 - initial version
    VERSION = '1.0'

    def __init__(self):
        self.stub = resources_rpc.ResourcesPullRpcApi()
        target = oslo_messaging.Target(
            topic=trunk_consts.TRUNK_BASE_TOPIC,
            version=self.VERSION,
            namespace=trunk_consts.TRUNK_BASE_NAMESPACE)
        self.rpc_client = n_rpc.get_client(target)

    @log_helpers.log_method_call
    def get_trunk_details(self, context, parent_port_id):
        """Get information about the trunk for the given parent port."""
        return self.stub.pull(context, resources.TRUNK, parent_port_id)

    @log_helpers.log_method_call
    def update_trunk_status(self, context, trunk_id, status):
        """Update the trunk status to reflect outcome of data plane wiring."""
        return self.rpc_client.prepare().call(
            context, 'update_trunk_status',
            trunk_id=trunk_id, status=status)

    @log_helpers.log_method_call
    def update_subport_bindings(self, context, subports):
        """Update subport bindings to match parent port host binding."""
        return self.rpc_client.prepare().call(
            context, 'update_subport_bindings', subports=subports)
