Merge "Remove HyperVNeutronPlugin"
This commit is contained in:
commit
0f04be6e5e
@ -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