Remove HyperVNeutronPlugin

HyperVNeutronPlugin is hereby removed, as it is outdated
and it is no longer needed, thanks to Ml2Plugin. All of its
users should switch to Ml2Plugin.

The database tables 'hyperv_vlan_allocations' and
'hyperv_network_bindings' will no longer be used, since their
consumer will be removed.

Adds database migration script which will move all the existent
entries in the mentioned tables to their Ml2 equivalent ones.

Partially-implements: blueprint core-vendor-decomposition

Change-Id: If02c4761d9653dcd81fc0043ecce86a3b149dd99
This commit is contained in:
Claudiu Belu 2015-02-05 22:08:27 +02:00
parent 38499d1a67
commit 3cceb99dfd
12 changed files with 150 additions and 1052 deletions

View File

@ -1,63 +0,0 @@
[hyperv]
# (StrOpt) Type of network to allocate for tenant networks. The
# default value 'local' is useful only for single-box testing and
# provides no connectivity between hosts. You MUST either change this
# to 'vlan' and configure network_vlan_ranges below or to 'flat'.
# Set to 'none' to disable creation of tenant networks.
#
# tenant_network_type = local
# Example: tenant_network_type = vlan
# (ListOpt) Comma-separated list of
# <physical_network>[:<vlan_min>:<vlan_max>] tuples enumerating ranges
# of VLAN IDs on named physical networks that are available for
# allocation. All physical networks listed are available for flat and
# VLAN provider network creation. Specified ranges of VLAN IDs are
# available for tenant network allocation if tenant_network_type is
# 'vlan'. If empty, only gre and local networks may be created.
#
# network_vlan_ranges =
# Example: network_vlan_ranges = physnet1:1000:2999
[agent]
# Agent's polling interval in seconds
# polling_interval = 2
# (ListOpt) Comma separated list of <physical_network>:<vswitch>
# where the physical networks can be expressed with wildcards,
# e.g.: ."*:external".
# The referred external virtual switches need to be already present on
# the Hyper-V server.
# If a given physical network name will not match any value in the list
# the plugin will look for a virtual switch with the same name.
#
# physical_network_vswitch_mappings = *:external
# Example: physical_network_vswitch_mappings = net1:external1,net2:external2
# (StrOpt) Private virtual switch name used for local networking.
#
# local_network_vswitch = private
# Example: local_network_vswitch = custom_vswitch
# (BoolOpt) Enables metrics collections for switch ports by using Hyper-V's
# metric APIs. Collected data can by retrieved by other apps and services,
# e.g.: Ceilometer. Requires Hyper-V / Windows Server 2012 and above.
#
# enable_metrics_collection = False
#-----------------------------------------------------------------------------
# Sample Configurations.
#-----------------------------------------------------------------------------
#
# Neutron server:
#
# [HYPERV]
# tenant_network_type = vlan
# network_vlan_ranges = default:2000:3999
#
# Agent running on Hyper-V node:
#
# [AGENT]
# polling_interval = 2
# physical_network_vswitch_mappings = *:external
# local_network_vswitch = private

View File

@ -0,0 +1,149 @@
# Copyright 2015 OpenStack Foundation
#
# 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.
#
"""Remove Hyper-V Neutron Plugin
Migrates the contents of the tables 'hyperv_vlan_allocations' and
'hyperv_network_bindings' to 'ml2_vlan_allocations' and 'ml2_network_segments'
respectively, and then removes the tables.
Thse tables are used by HyperVNeutronPlugin, which will be removed.
Revision ID: 2b801560a332
Revises: 4119216b7365
Create Date: 2015-02-12 09:23:40.346104
"""
# revision identifiers, used by Alembic.
revision = '2b801560a332'
down_revision = '2d2a8a565438'
from alembic import op
import sqlalchemy as sa
from sqlalchemy.sql import expression as sa_expr
from neutron.extensions import portbindings
from neutron.openstack.common import uuidutils
from neutron.plugins.common import constants as p_const
FLAT_VLAN_ID = -1
LOCAL_VLAN_ID = -2
HYPERV = 'hyperv'
# Duplicated from neutron.plugins.linuxbridge.common.constants to
# avoid being dependent on it, as it will eventually be removed.
def _interpret_vlan_id(vlan_id):
"""Return (network_type, segmentation_id) tuple for encoded vlan_id."""
if vlan_id == LOCAL_VLAN_ID:
return (p_const.TYPE_LOCAL, None)
elif vlan_id == FLAT_VLAN_ID:
return (p_const.TYPE_FLAT, None)
else:
return (p_const.TYPE_VLAN, vlan_id)
def _migrate_segment_dict(binding):
binding['id'] = uuidutils.generate_uuid()
vlan_id = binding.pop('segmentation_id')
network_type, segmentation_id = _interpret_vlan_id(vlan_id)
binding['network_type'] = network_type
binding['segmentation_id'] = segmentation_id
def _migrate_vlan_allocations():
# Code similar to migrate_to_ml2.BaseMigrateToMl2.migrate_vlan_allocations
op.execute('INSERT INTO ml2_vlan_allocations '
'SELECT physical_network, vlan_id, allocated '
'FROM hyperv_vlan_allocations '
'WHERE allocated = TRUE')
def _migrate_network_segments(engine):
# Code similar to migrate_to_ml2.BaseMigrateToMl2.migrate_network_segments
source_table = sa_expr.table('hyperv_network_bindings')
source_segments = engine.execute(
sa_expr.select(['*'], from_obj=source_table))
ml2_segments = [dict(x) for x in source_segments]
for segment in ml2_segments:
_migrate_segment_dict(segment)
if ml2_segments:
ml2_network_segments = sa_expr.table('ml2_network_segments')
op.execute(ml2_network_segments.insert(), ml2_segments)
def _get_port_segment_map(engine):
# Code from migrate_to_ml2.BaseMigrateToMl2.get_port_segment_map
port_segments = engine.execute("""
SELECT ports_network.port_id, ml2_network_segments.id AS segment_id
FROM ml2_network_segments, (
SELECT portbindingports.port_id, ports.network_id
FROM portbindingports, ports
WHERE portbindingports.port_id = ports.id
) AS ports_network
WHERE ml2_network_segments.network_id = ports_network.network_id
""")
return dict(x for x in port_segments)
def _migrate_port_bindings(engine):
# Code similar to migrate_to_ml2.BaseMigrateToMl2.migrate_port_bindings
port_segment_map = _get_port_segment_map(engine)
port_binding_ports = sa_expr.table('portbindingports')
source_bindings = engine.execute(
sa_expr.select(['*'], from_obj=port_binding_ports))
ml2_bindings = [dict(x) for x in source_bindings]
for binding in ml2_bindings:
binding['vif_type'] = portbindings.VIF_TYPE_HYPERV
binding['driver'] = HYPERV
segment = port_segment_map.get(binding['port_id'])
if segment:
binding['segment'] = segment
if ml2_bindings:
ml2_port_bindings = sa_expr.table('ml2_port_bindings')
op.execute(ml2_port_bindings.insert(), ml2_bindings)
def upgrade():
bind = op.get_bind()
_migrate_vlan_allocations()
_migrate_network_segments(bind)
_migrate_port_bindings(bind)
op.drop_table('hyperv_vlan_allocations')
op.drop_table('hyperv_network_bindings')
def downgrade():
op.create_table(
'hyperv_vlan_allocations',
sa.Column('physical_network', sa.String(length=64), nullable=False),
sa.Column('vlan_id', sa.Integer(), autoincrement=False,
nullable=False),
sa.Column('allocated', sa.Boolean(), nullable=False),
sa.PrimaryKeyConstraint('physical_network', 'vlan_id'))
op.create_table(
'hyperv_network_bindings',
sa.Column('network_id', sa.String(length=36), nullable=False),
sa.Column('network_type', sa.String(length=32), nullable=False),
sa.Column('physical_network', sa.String(length=64), nullable=True),
sa.Column('segmentation_id', sa.Integer(), nullable=True),
sa.ForeignKeyConstraint(['network_id'], ['networks.id'],
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('network_id'))

View File

@ -1 +1 @@
2d2a8a565438
2b801560a332

View File

@ -48,7 +48,6 @@ from neutron.plugins.brocade.db import models as brocade_models # noqa
from neutron.plugins.cisco.db.l3 import l3_models # noqa
from neutron.plugins.cisco.db import n1kv_models_v2 # noqa
from neutron.plugins.cisco.db import network_models_v2 # noqa
from neutron.plugins.hyperv import model # noqa
from neutron.plugins.linuxbridge.db import l2network_models_v2 # noqa
from neutron.plugins.metaplugin import meta_models_v2 # noqa
from neutron.plugins.ml2.drivers.arista import db # noqa

View File

@ -1,70 +0,0 @@
# 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.
import oslo_messaging
from neutron.common import rpc as n_rpc
from neutron.common import topics
from neutron.plugins.hyperv.common import constants
class AgentNotifierApi(object):
'''Agent side of the openvswitch rpc API.
API version history:
1.0 - Initial version.
'''
def __init__(self, topic):
self.topic = topic
self.topic_network_delete = topics.get_topic_name(topic,
topics.NETWORK,
topics.DELETE)
self.topic_port_update = topics.get_topic_name(topic,
topics.PORT,
topics.UPDATE)
self.topic_port_delete = topics.get_topic_name(topic,
topics.PORT,
topics.DELETE)
self.topic_tunnel_update = topics.get_topic_name(topic,
constants.TUNNEL,
topics.UPDATE)
target = oslo_messaging.Target(topic=topic, version='1.0')
self.client = n_rpc.get_client(target)
def network_delete(self, context, network_id):
cctxt = self.client.prepare(topic=self.topic_network_delete,
fanout=True)
cctxt.cast(context, 'network_delete', network_id=network_id)
def port_update(self, context, port, network_type, segmentation_id,
physical_network):
cctxt = self.client.prepare(topic=self.topic_port_update,
fanout=True)
cctxt.cast(context, 'port_update', port=port,
network_type=network_type, segmentation_id=segmentation_id,
physical_network=physical_network)
def port_delete(self, context, port_id):
cctxt = self.client.prepare(topic=self.topic_port_delete,
fanout=True)
cctxt.cast(context, 'port_delete', port_id=port_id)
def tunnel_update(self, context, tunnel_ip, tunnel_id):
cctxt = self.client.prepare(topic=self.topic_tunnel_update,
fanout=True)
cctxt.cast(context, 'tunnel_update', tunnel_ip=tunnel_ip,
tunnel_id=tunnel_id)

View File

@ -1,214 +0,0 @@
# 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.
from six import moves
from sqlalchemy.orm import exc
from neutron.common import exceptions as n_exc
import neutron.db.api as db_api
from neutron.db import models_v2
from neutron.i18n import _LW
from neutron.openstack.common import log as logging
from neutron.plugins.hyperv.common import constants
from neutron.plugins.hyperv import model as hyperv_model
LOG = logging.getLogger(__name__)
class HyperVPluginDB(object):
def reserve_vlan(self, session):
with session.begin(subtransactions=True):
alloc_q = session.query(hyperv_model.VlanAllocation)
alloc_q = alloc_q.filter_by(allocated=False)
alloc = alloc_q.first()
if alloc:
LOG.debug("Reserving vlan %(vlan_id)s on physical network "
"%(physical_network)s from pool",
{'vlan_id': alloc.vlan_id,
'physical_network': alloc.physical_network})
alloc.allocated = True
return (alloc.physical_network, alloc.vlan_id)
raise n_exc.NoNetworkAvailable()
def reserve_flat_net(self, session):
with session.begin(subtransactions=True):
alloc_q = session.query(hyperv_model.VlanAllocation)
alloc_q = alloc_q.filter_by(allocated=False,
vlan_id=constants.FLAT_VLAN_ID)
alloc = alloc_q.first()
if alloc:
LOG.debug("Reserving flat physical network "
"%(physical_network)s from pool",
{'physical_network': alloc.physical_network})
alloc.allocated = True
return alloc.physical_network
raise n_exc.NoNetworkAvailable()
def reserve_specific_vlan(self, session, physical_network, vlan_id):
with session.begin(subtransactions=True):
try:
alloc_q = session.query(hyperv_model.VlanAllocation)
alloc_q = alloc_q.filter_by(
physical_network=physical_network,
vlan_id=vlan_id)
alloc = alloc_q.one()
if alloc.allocated:
if vlan_id == constants.FLAT_VLAN_ID:
raise n_exc.FlatNetworkInUse(
physical_network=physical_network)
else:
raise n_exc.VlanIdInUse(
vlan_id=vlan_id,
physical_network=physical_network)
LOG.debug("Reserving specific vlan %(vlan_id)s on physical "
"network %(physical_network)s from pool",
{'vlan_id': vlan_id,
'physical_network': physical_network})
alloc.allocated = True
except exc.NoResultFound:
raise n_exc.NoNetworkAvailable()
def reserve_specific_flat_net(self, session, physical_network):
return self.reserve_specific_vlan(session, physical_network,
constants.FLAT_VLAN_ID)
def add_network_binding(self, session, network_id, network_type,
physical_network, segmentation_id):
with session.begin(subtransactions=True):
binding = hyperv_model.NetworkBinding(
network_id, network_type,
physical_network,
segmentation_id)
session.add(binding)
def get_port(self, port_id):
session = db_api.get_session()
try:
port = session.query(models_v2.Port).filter_by(id=port_id).one()
except exc.NoResultFound:
port = None
return port
def get_network_binding(self, session, network_id):
session = session or db_api.get_session()
try:
binding_q = session.query(hyperv_model.NetworkBinding)
binding_q = binding_q.filter_by(network_id=network_id)
return binding_q.one()
except exc.NoResultFound:
return
def set_port_status(self, port_id, status):
session = db_api.get_session()
try:
port = session.query(models_v2.Port).filter_by(id=port_id).one()
port['status'] = status
session.merge(port)
session.flush()
except exc.NoResultFound:
raise n_exc.PortNotFound(port_id=port_id)
def release_vlan(self, session, physical_network, vlan_id):
with session.begin(subtransactions=True):
try:
alloc_q = session.query(hyperv_model.VlanAllocation)
alloc_q = alloc_q.filter_by(physical_network=physical_network,
vlan_id=vlan_id)
alloc = alloc_q.one()
alloc.allocated = False
#session.delete(alloc)
LOG.debug("Releasing vlan %(vlan_id)s on physical network "
"%(physical_network)s",
{'vlan_id': vlan_id,
'physical_network': physical_network})
except exc.NoResultFound:
LOG.warning(_LW("vlan_id %(vlan_id)s on physical network "
"%(physical_network)s not found"),
{'vlan_id': vlan_id,
'physical_network': physical_network})
def _add_missing_allocatable_vlans(self, session, vlan_ids,
physical_network):
for vlan_id in sorted(vlan_ids):
alloc = hyperv_model.VlanAllocation(
physical_network, vlan_id)
session.add(alloc)
def _remove_non_allocatable_vlans(self, session,
physical_network,
vlan_ids,
allocations):
if physical_network in allocations:
for alloc in allocations[physical_network]:
try:
# see if vlan is allocatable
vlan_ids.remove(alloc.vlan_id)
except KeyError:
# it's not allocatable, so check if its allocated
if not alloc.allocated:
# it's not, so remove it from table
LOG.debug(
"Removing vlan %(vlan_id)s on physical network "
"%(physical_network)s from pool",
{'vlan_id': alloc.vlan_id,
'physical_network': physical_network})
session.delete(alloc)
del allocations[physical_network]
def _remove_unconfigured_vlans(self, session, allocations):
for allocs in allocations.itervalues():
for alloc in allocs:
if not alloc.allocated:
LOG.debug("Removing vlan %(vlan_id)s on physical "
"network %(physical_network)s from pool",
{'vlan_id': alloc.vlan_id,
'physical_network': alloc.physical_network})
session.delete(alloc)
def sync_vlan_allocations(self, network_vlan_ranges):
"""Synchronize vlan_allocations table with configured VLAN ranges."""
session = db_api.get_session()
with session.begin():
# get existing allocations for all physical networks
allocations = dict()
allocs_q = session.query(hyperv_model.VlanAllocation)
for alloc in allocs_q:
allocations.setdefault(alloc.physical_network,
set()).add(alloc)
# process vlan ranges for each configured physical network
for physical_network, vlan_ranges in network_vlan_ranges.items():
# determine current configured allocatable vlans for this
# physical network
vlan_ids = set()
for vlan_range in vlan_ranges:
vlan_ids |= set(moves.xrange(vlan_range[0],
vlan_range[1] + 1))
# remove from table unallocated vlans not currently allocatable
self._remove_non_allocatable_vlans(session,
physical_network,
vlan_ids,
allocations)
# add missing allocatable vlans to table
self._add_missing_allocatable_vlans(session, vlan_ids,
physical_network)
# remove from table unallocated vlans for any unconfigured physical
# networks
self._remove_unconfigured_vlans(session, allocations)

View File

@ -1,336 +0,0 @@
# 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.
from oslo_config import cfg
from neutron.api.rpc.handlers import dhcp_rpc
from neutron.api.rpc.handlers import l3_rpc
from neutron.api.rpc.handlers import metadata_rpc
from neutron.api.v2 import attributes
from neutron.common import exceptions as n_exc
from neutron.common import rpc as n_rpc
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.i18n import _LI
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.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 = n_rpc.create_connection(new=True)
self.notifier = agent_notifier_api.AgentNotifierApi(
topics.AGENT)
self.endpoints = [rpc_callbacks.HyperVRpcCallbacks(self.notifier),
dhcp_rpc.DhcpRpcCallback(),
l3_rpc.L3RpcCallback(),
agents_db.AgentExtRpcCallback(),
metadata_rpc.MetadataRpcCallback()]
for svc_topic in self.service_topics.values():
self.conn.create_consumer(svc_topic, self.endpoints, 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(_LI("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)

View File

@ -1,52 +0,0 @@
# 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.
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
from neutron.db import model_base
class VlanAllocation(model_base.BASEV2):
"""Represents allocation state of vlan_id on physical network."""
__tablename__ = 'hyperv_vlan_allocations'
physical_network = Column(String(64), nullable=False, primary_key=True)
vlan_id = Column(Integer, nullable=False, primary_key=True,
autoincrement=False)
allocated = Column(Boolean, nullable=False)
def __init__(self, physical_network, vlan_id):
self.physical_network = physical_network
self.vlan_id = vlan_id
self.allocated = False
class NetworkBinding(model_base.BASEV2):
"""Represents binding of virtual network to physical realization."""
__tablename__ = 'hyperv_network_bindings'
network_id = Column(String(36),
ForeignKey('networks.id', ondelete="CASCADE"),
primary_key=True)
network_type = Column(String(32), nullable=False)
physical_network = Column(String(64))
segmentation_id = Column(Integer)
def __init__(self, network_id, network_type, physical_network,
segmentation_id):
self.network_id = network_id
self.network_type = network_type
self.physical_network = physical_network
self.segmentation_id = segmentation_id

View File

@ -1,99 +0,0 @@
# 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.
import oslo_messaging
from neutron.common import constants as q_const
from neutron.openstack.common import log as logging
from neutron.plugins.hyperv import db as hyperv_db
LOG = logging.getLogger(__name__)
class HyperVRpcCallbacks(object):
# history
# 1.1 Support Security Group RPC
# 1.2 Support get_devices_details_list
target = oslo_messaging.Target(version='1.2')
def __init__(self, notifier):
super(HyperVRpcCallbacks, self).__init__()
self.notifier = notifier
self._db = hyperv_db.HyperVPluginDB()
def get_device_details(self, rpc_context, **kwargs):
"""Agent requests device details."""
agent_id = kwargs.get('agent_id')
device = kwargs.get('device')
LOG.debug("Device %(device)s details requested from %(agent_id)s",
{'device': device, 'agent_id': agent_id})
port = self._db.get_port(device)
if port:
binding = self._db.get_network_binding(None, port['network_id'])
entry = {'device': device,
'network_id': port['network_id'],
'port_id': port['id'],
'admin_state_up': port['admin_state_up'],
'network_type': binding.network_type,
'segmentation_id': binding.segmentation_id,
'physical_network': binding.physical_network}
# Set the port status to UP
self._db.set_port_status(port['id'], q_const.PORT_STATUS_ACTIVE)
else:
entry = {'device': device}
LOG.debug("%s can not be found in database", device)
return entry
def get_devices_details_list(self, rpc_context, **kwargs):
return [
self.get_device_details(
rpc_context,
device=device,
**kwargs
)
for device in kwargs.pop('devices', [])
]
def update_device_down(self, rpc_context, **kwargs):
"""Device no longer exists on agent."""
# TODO(garyk) - live migration and port status
agent_id = kwargs.get('agent_id')
device = kwargs.get('device')
LOG.debug("Device %(device)s no longer exists on %(agent_id)s",
{'device': device, 'agent_id': agent_id})
port = self._db.get_port(device)
if port:
entry = {'device': device,
'exists': True}
# Set port status to DOWN
self._db.set_port_status(port['id'], q_const.PORT_STATUS_DOWN)
else:
entry = {'device': device,
'exists': False}
LOG.debug("%s can not be found in database", device)
return entry
def tunnel_sync(self, rpc_context, **kwargs):
"""Tunnel sync.
Dummy function for ovs agent running on Linux to
work with Hyper-V plugin and agent.
"""
entry = dict()
entry['tunnels'] = {}
# Return the list of tunnels IP's to the agent
return entry

View File

@ -1,67 +0,0 @@
# Copyright 2013 Cloudbase Solutions SRL
# Copyright 2013 Pedro Navarro Perez
# 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.
import contextlib
from oslo_config import cfg
from neutron import context
from neutron.extensions import portbindings
from neutron import manager
from neutron.tests.unit import test_db_plugin as test_plugin
class HyperVNeutronPluginTestCase(test_plugin.NeutronDbPluginV2TestCase):
_plugin_name = ('neutron.plugins.hyperv.'
'hyperv_neutron_plugin.HyperVNeutronPlugin')
def setUp(self):
super(HyperVNeutronPluginTestCase, self).setUp(self._plugin_name)
class TestHyperVVirtualSwitchBasicGet(
test_plugin.TestBasicGet, HyperVNeutronPluginTestCase):
pass
class TestHyperVVirtualSwitchV2HTTPResponse(
test_plugin.TestV2HTTPResponse, HyperVNeutronPluginTestCase):
pass
class TestHyperVVirtualSwitchPortsV2(
test_plugin.TestPortsV2, HyperVNeutronPluginTestCase):
def test_port_vif_details(self):
with self.port(name='name') as port:
self.assertEqual(port['port']['binding:vif_type'],
portbindings.VIF_TYPE_HYPERV)
def test_ports_vif_details(self):
cfg.CONF.set_default('allow_overlapping_ips', True)
plugin = manager.NeutronManager.get_plugin()
with contextlib.nested(self.port(), self.port()) as (port1, port2):
ctx = context.get_admin_context()
ports = plugin.get_ports(ctx)
self.assertEqual(len(ports), 2)
for port in ports:
self.assertEqual(port['binding:vif_type'],
portbindings.VIF_TYPE_HYPERV)
class TestHyperVVirtualSwitchNetworksV2(
test_plugin.TestNetworksV2, HyperVNeutronPluginTestCase):
pass

View File

@ -1,147 +0,0 @@
# Copyright 2013 Cloudbase Solutions SRL
# Copyright 2013 Pedro Navarro Perez
# 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.
"""
Unit Tests for hyperv neutron rpc
"""
import contextlib
import mock
from oslo_context import context as oslo_context
from neutron.agent import rpc as agent_rpc
from neutron.common import topics
from neutron.plugins.hyperv import agent_notifier_api as ana
from neutron.plugins.hyperv.common import constants
from neutron.tests import base
class rpcHyperVApiTestCase(base.BaseTestCase):
def _test_hyperv_neutron_api(
self, rpcapi, topic, method, rpc_method, **kwargs):
ctxt = oslo_context.RequestContext('fake_user', 'fake_project')
expected_retval = 'foo' if rpc_method == 'call' else None
expected_version = kwargs.pop('version', None)
fanout = kwargs.pop('fanout', False)
with contextlib.nested(
mock.patch.object(rpcapi.client, rpc_method),
mock.patch.object(rpcapi.client, 'prepare'),
) as (
rpc_mock, prepare_mock
):
prepare_mock.return_value = rpcapi.client
rpc_mock.return_value = expected_retval
retval = getattr(rpcapi, method)(ctxt, **kwargs)
self.assertEqual(retval, expected_retval)
prepare_args = {}
if expected_version:
prepare_args['version'] = expected_version
if fanout:
prepare_args['fanout'] = True
if topic:
prepare_args['topic'] = topic
prepare_mock.assert_called_once_with(**prepare_args)
rpc_mock.assert_called_once_with(ctxt, method, **kwargs)
def test_delete_network(self):
rpcapi = ana.AgentNotifierApi(topics.AGENT)
self._test_hyperv_neutron_api(
rpcapi,
topics.get_topic_name(
topics.AGENT,
topics.NETWORK,
topics.DELETE),
'network_delete', rpc_method='cast', fanout=True,
network_id='fake_request_spec')
def test_port_update(self):
rpcapi = ana.AgentNotifierApi(topics.AGENT)
self._test_hyperv_neutron_api(
rpcapi,
topics.get_topic_name(
topics.AGENT,
topics.PORT,
topics.UPDATE),
'port_update', rpc_method='cast', fanout=True,
port='fake_port',
network_type='fake_network_type',
segmentation_id='fake_segmentation_id',
physical_network='fake_physical_network')
def test_port_delete(self):
rpcapi = ana.AgentNotifierApi(topics.AGENT)
self._test_hyperv_neutron_api(
rpcapi,
topics.get_topic_name(
topics.AGENT,
topics.PORT,
topics.DELETE),
'port_delete', rpc_method='cast', fanout=True,
port_id='port_id')
def test_tunnel_update(self):
rpcapi = ana.AgentNotifierApi(topics.AGENT)
self._test_hyperv_neutron_api(
rpcapi,
topics.get_topic_name(
topics.AGENT,
constants.TUNNEL,
topics.UPDATE),
'tunnel_update', rpc_method='cast', fanout=True,
tunnel_ip='fake_ip', tunnel_id='fake_id')
def test_device_details(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
self._test_hyperv_neutron_api(
rpcapi, None,
'get_device_details', rpc_method='call',
device='fake_device',
agent_id='fake_agent_id',
host='fake_host')
def test_devices_details_list(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
self._test_hyperv_neutron_api(
rpcapi, None,
'get_devices_details_list', rpc_method='call',
devices=['fake_device1', 'fake_device2'],
agent_id='fake_agent_id', host='fake_host',
version='1.3')
def test_update_device_down(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
self._test_hyperv_neutron_api(
rpcapi, None,
'update_device_down', rpc_method='call',
device='fake_device',
agent_id='fake_agent_id',
host='fake_host')
def test_tunnel_sync(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
self._test_hyperv_neutron_api(
rpcapi, None,
'tunnel_sync', rpc_method='call',
tunnel_ip='fake_tunnel_ip',
tunnel_type=None,
host='fake_host',
version='1.4')

View File

@ -54,7 +54,6 @@ data_files =
etc/neutron/plugins/cisco/cisco_router_plugin.ini
etc/neutron/plugins/cisco/cisco_vpn_agent.ini
etc/neutron/plugins/embrane = etc/neutron/plugins/embrane/heleos_conf.ini
etc/neutron/plugins/hyperv = etc/neutron/plugins/hyperv/hyperv_neutron_plugin.ini
etc/neutron/plugins/ibm = etc/neutron/plugins/ibm/sdnve_neutron_plugin.ini
etc/neutron/plugins/linuxbridge = etc/neutron/plugins/linuxbridge/linuxbridge_conf.ini
etc/neutron/plugins/metaplugin = etc/neutron/plugins/metaplugin/metaplugin.ini
@ -121,7 +120,6 @@ neutron.core_plugins =
brocade = neutron.plugins.brocade.NeutronPlugin:BrocadePluginV2
cisco = neutron.plugins.cisco.network_plugin:PluginV2
embrane = neutron.plugins.embrane.plugins.embrane_ml2_plugin:EmbraneMl2Plugin
hyperv = neutron.plugins.hyperv.hyperv_neutron_plugin:HyperVNeutronPlugin
ibm = neutron.plugins.ibm.sdnve_neutron_plugin:SdnvePluginV2
midonet = neutron.plugins.midonet.plugin:MidonetPluginV2
ml2 = neutron.plugins.ml2.plugin:Ml2Plugin