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:
parent
38499d1a67
commit
3cceb99dfd
@ -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
|
@ -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'))
|
@ -1 +1 @@
|
||||
2d2a8a565438
|
||||
2b801560a332
|
||||
|
@ -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
|
||||
|
@ -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)
|
@ -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)
|
@ -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)
|
@ -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
|
@ -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
|
@ -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
|
@ -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')
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user