# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2012, Red Hat, Inc.
#
#    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.

"""
Client side of the network RPC API.
"""

from nova import config
from nova import flags
from nova.openstack.common import jsonutils
from nova.openstack.common import rpc
from nova.openstack.common.rpc import proxy as rpc_proxy

CONF = config.CONF


class NetworkAPI(rpc_proxy.RpcProxy):
    '''Client side of the network rpc API.

    API version history:

        1.0 - Initial version.
        1.1 - Adds migrate_instance_[start|finish]
        1.2 - Make migrate_instance_[start|finish] a little more flexible
    '''

    #
    # NOTE(russellb): This is the default minimum version that the server
    # (manager) side must implement unless otherwise specified using a version
    # argument to self.call()/cast()/etc. here.  It should be left as X.0 where
    # X is the current major API version (1.0, 2.0, ...).  For more information
    # about rpc API versioning, see the docs in
    # openstack/common/rpc/dispatcher.py.
    #
    BASE_RPC_API_VERSION = '1.0'

    def __init__(self, topic=None):
        topic = topic if topic else CONF.network_topic
        super(NetworkAPI, self).__init__(
                topic=topic,
                default_version=self.BASE_RPC_API_VERSION)

    def get_all_networks(self, ctxt):
        return self.call(ctxt, self.make_msg('get_all_networks'))

    def get_network(self, ctxt, network_uuid):
        return self.call(ctxt, self.make_msg('get_network',
                network_uuid=network_uuid))

    # TODO(russellb): Convert this to named arguments.  It's a pretty large
    # list, so unwinding it all is probably best done in its own patch so it's
    # easier to review.
    def create_networks(self, ctxt, **kwargs):
        return self.call(ctxt, self.make_msg('create_networks', **kwargs))

    def delete_network(self, ctxt, uuid, fixed_range):
        return self.call(ctxt, self.make_msg('delete_network',
                uuid=uuid, fixed_range=fixed_range))

    def disassociate_network(self, ctxt, network_uuid):
        return self.call(ctxt, self.make_msg('disassociate_network',
                network_uuid=network_uuid))

    def get_fixed_ip(self, ctxt, id):
        return self.call(ctxt, self.make_msg('get_fixed_ip', id=id))

    def get_fixed_ip_by_address(self, ctxt, address):
        return self.call(ctxt, self.make_msg('get_fixed_ip_by_address',
                address=address))

    def get_floating_ip(self, ctxt, id):
        return self.call(ctxt, self.make_msg('get_floating_ip', id=id))

    def get_floating_pools(self, ctxt):
        return self.call(ctxt, self.make_msg('get_floating_pools'))

    def get_floating_ip_by_address(self, ctxt, address):
        return self.call(ctxt, self.make_msg('get_floating_ip_by_address',
                address=address))

    def get_floating_ips_by_project(self, ctxt):
        return self.call(ctxt, self.make_msg('get_floating_ips_by_project'))

    def get_floating_ips_by_fixed_address(self, ctxt, fixed_address):
        return self.call(ctxt, self.make_msg(
                'get_floating_ips_by_fixed_address',
                fixed_address=fixed_address))

    def get_instance_id_by_floating_address(self, ctxt, address):
        return self.call(ctxt, self.make_msg(
                'get_instance_id_by_floating_address',
                address=address))

    def get_vifs_by_instance(self, ctxt, instance_id):
        # NOTE(vish): When the db calls are converted to store network
        #             data by instance_uuid, this should pass uuid instead.
        return self.call(ctxt, self.make_msg('get_vifs_by_instance',
                instance_id=instance_id))

    def get_vif_by_mac_address(self, ctxt, mac_address):
        return self.call(ctxt, self.make_msg('get_vif_by_mac_address',
                mac_address=mac_address))

    def allocate_floating_ip(self, ctxt, project_id, pool, auto_assigned):
        return self.call(ctxt, self.make_msg('allocate_floating_ip',
                project_id=project_id, pool=pool, auto_assigned=auto_assigned))

    def deallocate_floating_ip(self, ctxt, address, affect_auto_assigned):
        return self.call(ctxt, self.make_msg('deallocate_floating_ip',
            address=address, affect_auto_assigned=affect_auto_assigned))

    def associate_floating_ip(self, ctxt, floating_address, fixed_address,
                              affect_auto_assigned):
        return self.call(ctxt, self.make_msg('associate_floating_ip',
                floating_address=floating_address, fixed_address=fixed_address,
                affect_auto_assigned=affect_auto_assigned))

    def disassociate_floating_ip(self, ctxt, address, affect_auto_assigned):
        return self.call(ctxt, self.make_msg('disassociate_floating_ip',
                address=address, affect_auto_assigned=affect_auto_assigned))

    def allocate_for_instance(self, ctxt, instance_id, instance_uuid,
                              project_id, host, rxtx_factor, vpn,
                              requested_networks):
        return self.call(ctxt, self.make_msg('allocate_for_instance',
                instance_id=instance_id, instance_uuid=instance_uuid,
                project_id=project_id, host=host, rxtx_factor=rxtx_factor,
                vpn=vpn, requested_networks=requested_networks))

    def deallocate_for_instance(self, ctxt, instance_id, project_id, host):
        return self.call(ctxt, self.make_msg('deallocate_for_instance',
                instance_id=instance_id, project_id=project_id, host=host))

    def add_fixed_ip_to_instance(self, ctxt, instance_id, host, network_id):
        return self.call(ctxt, self.make_msg('add_fixed_ip_to_instance',
                instance_id=instance_id, host=host, network_id=network_id))

    def remove_fixed_ip_from_instance(self, ctxt, instance_id, host, address):
        return self.call(ctxt, self.make_msg('remove_fixed_ip_from_instance',
                instance_id=instance_id, host=host, address=address))

    def add_network_to_project(self, ctxt, project_id, network_uuid):
        return self.call(ctxt, self.make_msg('add_network_to_project',
                project_id=project_id, network_uuid=network_uuid))

    def get_instance_nw_info(self, ctxt, instance_id, instance_uuid,
                             rxtx_factor, host, project_id):
        return self.call(ctxt, self.make_msg('get_instance_nw_info',
                instance_id=instance_id, instance_uuid=instance_uuid,
                rxtx_factor=rxtx_factor, host=host, project_id=project_id))

    def validate_networks(self, ctxt, networks):
        return self.call(ctxt, self.make_msg('validate_networks',
                networks=networks))

    def get_instance_uuids_by_ip_filter(self, ctxt, filters):
        return self.call(ctxt, self.make_msg('get_instance_uuids_by_ip_filter',
                filters=filters))

    def get_dns_domains(self, ctxt):
        return self.call(ctxt, self.make_msg('get_dns_domains'))

    def add_dns_entry(self, ctxt, address, name, dns_type, domain):
        return self.call(ctxt, self.make_msg('add_dns_entry', address=address,
                name=name, dns_type=dns_type, domain=domain))

    def modify_dns_entry(self, ctxt, address, name, domain):
        return self.call(ctxt, self.make_msg('modify_dns_entry',
                address=address, name=name, domain=domain))

    def delete_dns_entry(self, ctxt, name, domain):
        return self.call(ctxt, self.make_msg('delete_dns_entry',
                name=name, domain=domain))

    def delete_dns_domain(self, ctxt, domain):
        return self.call(ctxt, self.make_msg('delete_dns_domain',
                domain=domain))

    def get_dns_entries_by_address(self, ctxt, address, domain):
        return self.call(ctxt, self.make_msg('get_dns_entries_by_address',
                address=address, domain=domain))

    def get_dns_entries_by_name(self, ctxt, name, domain):
        return self.call(ctxt, self.make_msg('get_dns_entries_by_name',
                name=name, domain=domain))

    def create_private_dns_domain(self, ctxt, domain, av_zone):
        return self.call(ctxt, self.make_msg('create_private_dns_domain',
                domain=domain, av_zone=av_zone))

    def create_public_dns_domain(self, ctxt, domain, project):
        return self.call(ctxt, self.make_msg('create_public_dns_domain',
                domain=domain, project=project))

    def setup_networks_on_host(self, ctxt, instance_id, host, teardown):
        # NOTE(tr3buchet): the call is just to wait for completion
        return self.call(ctxt, self.make_msg('setup_networks_on_host',
                instance_id=instance_id, host=host, teardown=teardown))

    def set_network_host(self, ctxt, network_ref):
        network_ref_p = jsonutils.to_primitive(network_ref)
        return self.call(ctxt, self.make_msg('set_network_host',
                network_ref=network_ref_p))

    def rpc_setup_network_on_host(self, ctxt, network_id, teardown, host):
        # NOTE(tr3buchet): the call is just to wait for completion
        return self.call(ctxt, self.make_msg('rpc_setup_network_on_host',
                network_id=network_id, teardown=teardown),
                topic=rpc.queue_get_for(ctxt, self.topic, host))

    # NOTE(russellb): Ideally this would not have a prefix of '_' since it is
    # a part of the rpc API. However, this is how it was being called when the
    # 1.0 API was being documented using this client proxy class.  It should be
    # changed if there was ever a 2.0.
    def _rpc_allocate_fixed_ip(self, ctxt, instance_id, network_id, address,
                               vpn, host):
        return self.call(ctxt, self.make_msg('_rpc_allocate_fixed_ip',
                instance_id=instance_id, network_id=network_id,
                address=address, vpn=vpn),
                topic=rpc.queue_get_for(ctxt, self.topic, host))

    def deallocate_fixed_ip(self, ctxt, address, host):
        return self.call(ctxt, self.make_msg('deallocate_fixed_ip',
                address=address, host=host),
                topic=rpc.queue_get_for(ctxt, self.topic, host))

    # NOTE(russellb): Ideally this would not have a prefix of '_' since it is
    # a part of the rpc API. However, this is how it was being called when the
    # 1.0 API was being documented using this client proxy class.  It should be
    # changed if there was ever a 2.0.
    def _associate_floating_ip(self, ctxt, floating_address, fixed_address,
                               interface, host):
        return self.call(ctxt, self.make_msg('_associate_floating_ip',
                floating_address=floating_address, fixed_address=fixed_address,
                interface=interface),
                topic=rpc.queue_get_for(ctxt, self.topic, host))

    # NOTE(russellb): Ideally this would not have a prefix of '_' since it is
    # a part of the rpc API. However, this is how it was being called when the
    # 1.0 API was being documented using this client proxy class.  It should be
    # changed if there was ever a 2.0.
    def _disassociate_floating_ip(self, ctxt, address, interface, host):
        return self.call(ctxt, self.make_msg('_disassociate_floating_ip',
                address=address, interface=interface),
                topic=rpc.queue_get_for(ctxt, self.topic, host))

    def lease_fixed_ip(self, ctxt, address, host):
        self.cast(ctxt, self.make_msg('lease_fixed_ip', address=address),
                  topic=rpc.queue_get_for(ctxt, self.topic, host))

    def release_fixed_ip(self, ctxt, address, host):
        self.cast(ctxt, self.make_msg('release_fixed_ip', address=address),
                  topic=rpc.queue_get_for(ctxt, self.topic, host))

    def migrate_instance_start(self, ctxt, instance_uuid, rxtx_factor,
                               project_id, source_compute, dest_compute,
                               floating_addresses, host=None):
        if host is not None:
            topic = rpc.queue_get_for(ctxt, self.topic, host)
        else:
            topic = self.topic
        return self.call(ctxt, self.make_msg(
                'migrate_instance_start',
                instance_uuid=instance_uuid,
                rxtx_factor=rxtx_factor,
                project_id=project_id,
                source=source_compute,
                dest=dest_compute,
                floating_addresses=floating_addresses),
                topic=topic,
                version='1.2')

    def migrate_instance_finish(self, ctxt, instance_uuid, rxtx_factor,
                                project_id, source_compute, dest_compute,
                                floating_addresses, host=None):
        if host is not None:
            topic = rpc.queue_get_for(ctxt, self.topic, host)
        else:
            topic = self.topic
        return self.call(ctxt, self.make_msg(
                'migrate_instance_finish',
                instance_uuid=instance_uuid,
                rxtx_factor=rxtx_factor,
                project_id=project_id,
                source=source_compute,
                dest=dest_compute,
                floating_addresses=floating_addresses),
                topic=topic,
                version='1.2')
