Now that we explicitly start multiple RPC servers, and each listener is served in a separate thread, renamed the method to reflect new behaviour. blueprint oslo-messaging Change-Id: I616f3a23e23e982e13f9b56ce417ca3623247f95
		
			
				
	
	
		
			334 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			334 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# vim: tabstop=4 shiftwidth=4 softtabstop=4
 | 
						|
 | 
						|
# Copyright 2013 Cloudbase Solutions SRL
 | 
						|
# 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.
 | 
						|
# @author: Alessandro Pilotti, Cloudbase Solutions Srl
 | 
						|
 | 
						|
from oslo.config import cfg
 | 
						|
 | 
						|
from neutron.api.v2 import attributes
 | 
						|
from neutron.common import exceptions as n_exc
 | 
						|
from neutron.common import rpc_compat
 | 
						|
from neutron.common import topics
 | 
						|
from neutron.db import agents_db
 | 
						|
from neutron.db import db_base_plugin_v2
 | 
						|
from neutron.db import external_net_db
 | 
						|
from neutron.db import l3_gwmode_db
 | 
						|
from neutron.db import portbindings_base
 | 
						|
from neutron.db import quota_db  # noqa
 | 
						|
from neutron.extensions import portbindings
 | 
						|
from neutron.extensions import providernet as provider
 | 
						|
from neutron.openstack.common import log as logging
 | 
						|
from neutron.plugins.common import constants as svc_constants
 | 
						|
from neutron.plugins.common import utils as plugin_utils
 | 
						|
from neutron.plugins.hyperv import agent_notifier_api
 | 
						|
from neutron.plugins.hyperv.common import constants
 | 
						|
from neutron.plugins.hyperv import db as hyperv_db
 | 
						|
from neutron.plugins.hyperv import rpc_callbacks
 | 
						|
 | 
						|
 | 
						|
DEFAULT_VLAN_RANGES = []
 | 
						|
 | 
						|
hyperv_opts = [
 | 
						|
    cfg.StrOpt('tenant_network_type', default='local',
 | 
						|
               help=_("Network type for tenant networks "
 | 
						|
                      "(local, flat, vlan or none)")),
 | 
						|
    cfg.ListOpt('network_vlan_ranges',
 | 
						|
                default=DEFAULT_VLAN_RANGES,
 | 
						|
                help=_("List of <physical_network>:<vlan_min>:<vlan_max> "
 | 
						|
                       "or <physical_network>")),
 | 
						|
]
 | 
						|
 | 
						|
cfg.CONF.register_opts(hyperv_opts, "HYPERV")
 | 
						|
 | 
						|
LOG = logging.getLogger(__name__)
 | 
						|
 | 
						|
 | 
						|
class BaseNetworkProvider(object):
 | 
						|
    def __init__(self):
 | 
						|
        self._db = hyperv_db.HyperVPluginDB()
 | 
						|
 | 
						|
    def create_network(self, session, attrs):
 | 
						|
        pass
 | 
						|
 | 
						|
    def delete_network(self, session, binding):
 | 
						|
        pass
 | 
						|
 | 
						|
    def extend_network_dict(self, network, binding):
 | 
						|
        pass
 | 
						|
 | 
						|
 | 
						|
class LocalNetworkProvider(BaseNetworkProvider):
 | 
						|
    def create_network(self, session, attrs):
 | 
						|
        network_type = attrs.get(provider.NETWORK_TYPE)
 | 
						|
        segmentation_id = attrs.get(provider.SEGMENTATION_ID)
 | 
						|
        if attributes.is_attr_set(segmentation_id):
 | 
						|
            msg = _("segmentation_id specified "
 | 
						|
                    "for %s network") % network_type
 | 
						|
            raise n_exc.InvalidInput(error_message=msg)
 | 
						|
        attrs[provider.SEGMENTATION_ID] = None
 | 
						|
 | 
						|
        physical_network = attrs.get(provider.PHYSICAL_NETWORK)
 | 
						|
        if attributes.is_attr_set(physical_network):
 | 
						|
            msg = _("physical_network specified "
 | 
						|
                    "for %s network") % network_type
 | 
						|
            raise n_exc.InvalidInput(error_message=msg)
 | 
						|
        attrs[provider.PHYSICAL_NETWORK] = None
 | 
						|
 | 
						|
    def extend_network_dict(self, network, binding):
 | 
						|
        network[provider.PHYSICAL_NETWORK] = None
 | 
						|
        network[provider.SEGMENTATION_ID] = None
 | 
						|
 | 
						|
 | 
						|
class FlatNetworkProvider(BaseNetworkProvider):
 | 
						|
    def create_network(self, session, attrs):
 | 
						|
        network_type = attrs.get(provider.NETWORK_TYPE)
 | 
						|
        segmentation_id = attrs.get(provider.SEGMENTATION_ID)
 | 
						|
        if attributes.is_attr_set(segmentation_id):
 | 
						|
            msg = _("segmentation_id specified "
 | 
						|
                    "for %s network") % network_type
 | 
						|
            raise n_exc.InvalidInput(error_message=msg)
 | 
						|
        segmentation_id = constants.FLAT_VLAN_ID
 | 
						|
        attrs[provider.SEGMENTATION_ID] = segmentation_id
 | 
						|
 | 
						|
        physical_network = attrs.get(provider.PHYSICAL_NETWORK)
 | 
						|
        if not attributes.is_attr_set(physical_network):
 | 
						|
            physical_network = self._db.reserve_flat_net(session)
 | 
						|
            attrs[provider.PHYSICAL_NETWORK] = physical_network
 | 
						|
        else:
 | 
						|
            self._db.reserve_specific_flat_net(session, physical_network)
 | 
						|
 | 
						|
    def delete_network(self, session, binding):
 | 
						|
        self._db.release_vlan(session, binding.physical_network,
 | 
						|
                              constants.FLAT_VLAN_ID)
 | 
						|
 | 
						|
    def extend_network_dict(self, network, binding):
 | 
						|
        network[provider.PHYSICAL_NETWORK] = binding.physical_network
 | 
						|
 | 
						|
 | 
						|
class VlanNetworkProvider(BaseNetworkProvider):
 | 
						|
    def create_network(self, session, attrs):
 | 
						|
        segmentation_id = attrs.get(provider.SEGMENTATION_ID)
 | 
						|
        if attributes.is_attr_set(segmentation_id):
 | 
						|
            physical_network = attrs.get(provider.PHYSICAL_NETWORK)
 | 
						|
            if not attributes.is_attr_set(physical_network):
 | 
						|
                msg = _("physical_network not provided")
 | 
						|
                raise n_exc.InvalidInput(error_message=msg)
 | 
						|
            self._db.reserve_specific_vlan(session, physical_network,
 | 
						|
                                           segmentation_id)
 | 
						|
        else:
 | 
						|
            (physical_network,
 | 
						|
             segmentation_id) = self._db.reserve_vlan(session)
 | 
						|
            attrs[provider.SEGMENTATION_ID] = segmentation_id
 | 
						|
            attrs[provider.PHYSICAL_NETWORK] = physical_network
 | 
						|
 | 
						|
    def delete_network(self, session, binding):
 | 
						|
        self._db.release_vlan(
 | 
						|
            session, binding.physical_network,
 | 
						|
            binding.segmentation_id)
 | 
						|
 | 
						|
    def extend_network_dict(self, network, binding):
 | 
						|
        network[provider.PHYSICAL_NETWORK] = binding.physical_network
 | 
						|
        network[provider.SEGMENTATION_ID] = binding.segmentation_id
 | 
						|
 | 
						|
 | 
						|
class HyperVNeutronPlugin(agents_db.AgentDbMixin,
 | 
						|
                          db_base_plugin_v2.NeutronDbPluginV2,
 | 
						|
                          external_net_db.External_net_db_mixin,
 | 
						|
                          l3_gwmode_db.L3_NAT_db_mixin,
 | 
						|
                          portbindings_base.PortBindingBaseMixin):
 | 
						|
 | 
						|
    # This attribute specifies whether the plugin supports or not
 | 
						|
    # bulk operations. Name mangling is used in order to ensure it
 | 
						|
    # is qualified by class
 | 
						|
    __native_bulk_support = True
 | 
						|
    supported_extension_aliases = ["provider", "external-net", "router",
 | 
						|
                                   "agent", "ext-gw-mode", "binding", "quotas"]
 | 
						|
 | 
						|
    def __init__(self, configfile=None):
 | 
						|
        self._db = hyperv_db.HyperVPluginDB()
 | 
						|
        self._db.initialize()
 | 
						|
        self.base_binding_dict = {
 | 
						|
            portbindings.VIF_TYPE: portbindings.VIF_TYPE_HYPERV}
 | 
						|
        portbindings_base.register_port_dict_function()
 | 
						|
        self._set_tenant_network_type()
 | 
						|
 | 
						|
        self._parse_network_vlan_ranges()
 | 
						|
        self._create_network_providers_map()
 | 
						|
        self._db.sync_vlan_allocations(self._network_vlan_ranges)
 | 
						|
 | 
						|
        self._setup_rpc()
 | 
						|
 | 
						|
    def _set_tenant_network_type(self):
 | 
						|
        tenant_network_type = cfg.CONF.HYPERV.tenant_network_type
 | 
						|
        if tenant_network_type not in [svc_constants.TYPE_LOCAL,
 | 
						|
                                       svc_constants.TYPE_FLAT,
 | 
						|
                                       svc_constants.TYPE_VLAN,
 | 
						|
                                       svc_constants.TYPE_NONE]:
 | 
						|
            msg = _(
 | 
						|
                "Invalid tenant_network_type: %s. "
 | 
						|
                "Agent terminated!") % tenant_network_type
 | 
						|
            raise n_exc.InvalidInput(error_message=msg)
 | 
						|
        self._tenant_network_type = tenant_network_type
 | 
						|
 | 
						|
    def _setup_rpc(self):
 | 
						|
        # RPC support
 | 
						|
        self.service_topics = {svc_constants.CORE: topics.PLUGIN,
 | 
						|
                               svc_constants.L3_ROUTER_NAT: topics.L3PLUGIN}
 | 
						|
        self.conn = rpc_compat.create_connection(new=True)
 | 
						|
        self.notifier = agent_notifier_api.AgentNotifierApi(
 | 
						|
            topics.AGENT)
 | 
						|
        self.callbacks = rpc_callbacks.HyperVRpcCallbacks(self.notifier)
 | 
						|
        self.dispatcher = self.callbacks.create_rpc_dispatcher()
 | 
						|
        for svc_topic in self.service_topics.values():
 | 
						|
            self.conn.create_consumer(svc_topic, self.dispatcher, fanout=False)
 | 
						|
        # Consume from all consumers in threads
 | 
						|
        self.conn.consume_in_threads()
 | 
						|
 | 
						|
    def _parse_network_vlan_ranges(self):
 | 
						|
        self._network_vlan_ranges = plugin_utils.parse_network_vlan_ranges(
 | 
						|
            cfg.CONF.HYPERV.network_vlan_ranges)
 | 
						|
        LOG.info(_("Network VLAN ranges: %s"), self._network_vlan_ranges)
 | 
						|
 | 
						|
    def _check_vlan_id_in_range(self, physical_network, vlan_id):
 | 
						|
        for r in self._network_vlan_ranges[physical_network]:
 | 
						|
            if vlan_id >= r[0] and vlan_id <= r[1]:
 | 
						|
                return True
 | 
						|
        return False
 | 
						|
 | 
						|
    def _create_network_providers_map(self):
 | 
						|
        self._network_providers_map = {
 | 
						|
            svc_constants.TYPE_LOCAL: LocalNetworkProvider(),
 | 
						|
            svc_constants.TYPE_FLAT: FlatNetworkProvider(),
 | 
						|
            svc_constants.TYPE_VLAN: VlanNetworkProvider()
 | 
						|
        }
 | 
						|
 | 
						|
    def _process_provider_create(self, context, session, attrs):
 | 
						|
        network_type = attrs.get(provider.NETWORK_TYPE)
 | 
						|
        network_type_set = attributes.is_attr_set(network_type)
 | 
						|
        if not network_type_set:
 | 
						|
            if self._tenant_network_type == svc_constants.TYPE_NONE:
 | 
						|
                raise n_exc.TenantNetworksDisabled()
 | 
						|
            network_type = self._tenant_network_type
 | 
						|
            attrs[provider.NETWORK_TYPE] = network_type
 | 
						|
 | 
						|
        if network_type not in self._network_providers_map:
 | 
						|
            msg = _("Network type %s not supported") % network_type
 | 
						|
            raise n_exc.InvalidInput(error_message=msg)
 | 
						|
        p = self._network_providers_map[network_type]
 | 
						|
        # Provider specific network creation
 | 
						|
        p.create_network(session, attrs)
 | 
						|
 | 
						|
    def create_network(self, context, network):
 | 
						|
        session = context.session
 | 
						|
        with session.begin(subtransactions=True):
 | 
						|
            network_attrs = network['network']
 | 
						|
            self._process_provider_create(context, session, network_attrs)
 | 
						|
 | 
						|
            net = super(HyperVNeutronPlugin, self).create_network(
 | 
						|
                context, network)
 | 
						|
 | 
						|
            network_type = network_attrs[provider.NETWORK_TYPE]
 | 
						|
            physical_network = network_attrs[provider.PHYSICAL_NETWORK]
 | 
						|
            segmentation_id = network_attrs[provider.SEGMENTATION_ID]
 | 
						|
 | 
						|
            self._db.add_network_binding(
 | 
						|
                session, net['id'], network_type,
 | 
						|
                physical_network, segmentation_id)
 | 
						|
 | 
						|
            self._process_l3_create(context, net, network['network'])
 | 
						|
            self._extend_network_dict_provider(context, net)
 | 
						|
 | 
						|
            LOG.debug(_("Created network: %s"), net['id'])
 | 
						|
            return net
 | 
						|
 | 
						|
    def _extend_network_dict_provider(self, context, network):
 | 
						|
        binding = self._db.get_network_binding(
 | 
						|
            context.session, network['id'])
 | 
						|
        network[provider.NETWORK_TYPE] = binding.network_type
 | 
						|
        p = self._network_providers_map[binding.network_type]
 | 
						|
        p.extend_network_dict(network, binding)
 | 
						|
 | 
						|
    def update_network(self, context, id, network):
 | 
						|
        provider._raise_if_updates_provider_attributes(network['network'])
 | 
						|
 | 
						|
        session = context.session
 | 
						|
        with session.begin(subtransactions=True):
 | 
						|
            net = super(HyperVNeutronPlugin, self).update_network(context, id,
 | 
						|
                                                                  network)
 | 
						|
            self._process_l3_update(context, net, network['network'])
 | 
						|
            self._extend_network_dict_provider(context, net)
 | 
						|
            return net
 | 
						|
 | 
						|
    def delete_network(self, context, id):
 | 
						|
        session = context.session
 | 
						|
        with session.begin(subtransactions=True):
 | 
						|
            binding = self._db.get_network_binding(session, id)
 | 
						|
            self._process_l3_delete(context, id)
 | 
						|
            super(HyperVNeutronPlugin, self).delete_network(context, id)
 | 
						|
            p = self._network_providers_map[binding.network_type]
 | 
						|
            p.delete_network(session, binding)
 | 
						|
        # the network_binding record is deleted via cascade from
 | 
						|
        # the network record, so explicit removal is not necessary
 | 
						|
        self.notifier.network_delete(context, id)
 | 
						|
 | 
						|
    def get_network(self, context, id, fields=None):
 | 
						|
        net = super(HyperVNeutronPlugin, self).get_network(context, id, None)
 | 
						|
        self._extend_network_dict_provider(context, net)
 | 
						|
        return self._fields(net, fields)
 | 
						|
 | 
						|
    def get_networks(self, context, filters=None, fields=None):
 | 
						|
        nets = super(HyperVNeutronPlugin, self).get_networks(
 | 
						|
            context, filters, None)
 | 
						|
        for net in nets:
 | 
						|
            self._extend_network_dict_provider(context, net)
 | 
						|
 | 
						|
        return [self._fields(net, fields) for net in nets]
 | 
						|
 | 
						|
    def create_port(self, context, port):
 | 
						|
        port_data = port['port']
 | 
						|
        port = super(HyperVNeutronPlugin, self).create_port(context, port)
 | 
						|
        self._process_portbindings_create_and_update(context,
 | 
						|
                                                     port_data,
 | 
						|
                                                     port)
 | 
						|
        return port
 | 
						|
 | 
						|
    def update_port(self, context, id, port):
 | 
						|
        original_port = super(HyperVNeutronPlugin, self).get_port(
 | 
						|
            context, id)
 | 
						|
        port_data = port['port']
 | 
						|
        port = super(HyperVNeutronPlugin, self).update_port(context, id, port)
 | 
						|
        self._process_portbindings_create_and_update(context,
 | 
						|
                                                     port_data,
 | 
						|
                                                     port)
 | 
						|
        if original_port['admin_state_up'] != port['admin_state_up']:
 | 
						|
            binding = self._db.get_network_binding(
 | 
						|
                None, port['network_id'])
 | 
						|
            self.notifier.port_update(context, port,
 | 
						|
                                      binding.network_type,
 | 
						|
                                      binding.segmentation_id,
 | 
						|
                                      binding.physical_network)
 | 
						|
        return port
 | 
						|
 | 
						|
    def delete_port(self, context, id, l3_port_check=True):
 | 
						|
        # if needed, check to see if this is a port owned by
 | 
						|
        # and l3-router.  If so, we should prevent deletion.
 | 
						|
        if l3_port_check:
 | 
						|
            self.prevent_l3_port_deletion(context, id)
 | 
						|
        self.disassociate_floatingips(context, id)
 | 
						|
 | 
						|
        super(HyperVNeutronPlugin, self).delete_port(context, id)
 | 
						|
        self.notifier.port_delete(context, id)
 |