plugin/ryu: make live-migration work with Ryu plugin
Fixes bug 1085861 Live-migration with Ryu plugin doesn't work because the unique constraint of PortBinding table is violated as follows. Now Ryu can handle those informations itself, so remove the table itself and simplify Ryu plugin. > ERROR [quantum.api.v2.resource] update failed > Traceback (most recent call last): > File "/quantum/api/v2/resource.py", line 95, in resource > result = method(request=request, **args) > File "/quantum/api/v2/base.py", line 397, in update > obj = obj_updater(request.context, id, **kwargs) > File "/quantum/plugins/ryu/ryu_quantum_plugin.py", line 226, in update_port > port_binding.dpid, port_binding.port_no)) > InvalidInput: Invalid input for operation: invalid (datapath_id, port_no) requested (00002eab88ec5140, 4), but (0000c2f19014c74a, 1). Change-Id: I7d993fd01125a27f6bf8e1b3fcac79ddcb8cb361
This commit is contained in:
parent
779093f3c2
commit
06b2b2b22a
@ -36,10 +36,13 @@ from quantum.common import config as logging_config
|
|||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.openstack.common.cfg import NoSuchGroupError
|
from quantum.openstack.common.cfg import NoSuchGroupError
|
||||||
from quantum.openstack.common.cfg import NoSuchOptError
|
from quantum.openstack.common.cfg import NoSuchOptError
|
||||||
from quantum.openstack.common import log as LOG
|
from quantum.openstack.common import log
|
||||||
from quantum.plugins.ryu.common import config
|
from quantum.plugins.ryu.common import config
|
||||||
|
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# This is copied of nova.flags._get_my_ip()
|
# This is copied of nova.flags._get_my_ip()
|
||||||
# Agent shouldn't depend on nova module
|
# Agent shouldn't depend on nova module
|
||||||
def _get_my_ip():
|
def _get_my_ip():
|
||||||
|
@ -187,46 +187,6 @@ class TunnelKey(object):
|
|||||||
return session.query(ryu_models_v2.TunnelKey).all()
|
return session.query(ryu_models_v2.TunnelKey).all()
|
||||||
|
|
||||||
|
|
||||||
def port_binding_create(port_id, net_id, dpid, port_no):
|
|
||||||
session = db.get_session()
|
|
||||||
session.query(models_v2.Port).filter(
|
|
||||||
models_v2.Port.network_id == net_id).filter(
|
|
||||||
models_v2.Port.id == port_id).one() # confirm port exists
|
|
||||||
with session.begin():
|
|
||||||
port_binding = ryu_models_v2.PortBinding(net_id, port_id,
|
|
||||||
dpid, port_no)
|
|
||||||
session.add(port_binding)
|
|
||||||
session.flush()
|
|
||||||
return port_binding
|
|
||||||
|
|
||||||
|
|
||||||
def port_binding_get(port_id, net_id):
|
|
||||||
session = db.get_session()
|
|
||||||
session.query(models_v2.Port).filter(
|
|
||||||
models_v2.Port.network_id == net_id).filter(
|
|
||||||
models_v2.Port.id == port_id).one() # confirm port exists
|
|
||||||
return session.query(ryu_models_v2.PortBinding).filter_by(
|
|
||||||
network_id=net_id).filter_by(port_id=port_id).one()
|
|
||||||
|
|
||||||
|
|
||||||
def port_binding_destroy(session, port_id, net_id):
|
|
||||||
try:
|
|
||||||
session.query(models_v2.Port).filter(
|
|
||||||
models_v2.Port.network_id == net_id).filter(
|
|
||||||
models_v2.Port.id == port_id).one() # confirm port exists
|
|
||||||
port_binding = session.query(ryu_models_v2.PortBinding).filter_by(
|
|
||||||
network_id=net_id).filter_by(port_id=port_id).one()
|
|
||||||
session.delete(port_binding)
|
|
||||||
session.flush()
|
|
||||||
return port_binding
|
|
||||||
except orm_exc.NoResultFound:
|
|
||||||
raise q_exc.PortNotFound(port_id=port_id, net_id=net_id)
|
|
||||||
|
|
||||||
|
|
||||||
def port_binding_all_list(session):
|
|
||||||
return session.query(ryu_models_v2.PortBinding).all()
|
|
||||||
|
|
||||||
|
|
||||||
def set_port_status(session, port_id, status):
|
def set_port_status(session, port_id, status):
|
||||||
try:
|
try:
|
||||||
port = session.query(models_v2.Port).filter_by(id=port_id).one()
|
port = session.query(models_v2.Port).filter_by(id=port_id).one()
|
||||||
|
@ -52,28 +52,3 @@ class TunnelKey(model_base.BASEV2):
|
|||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<TunnelKey(%s,%x)>" % (self.network_id, self.tunnel_key)
|
return "<TunnelKey(%s,%x)>" % (self.network_id, self.tunnel_key)
|
||||||
|
|
||||||
|
|
||||||
class PortBinding(model_base.BASEV2):
|
|
||||||
"""Represents Port binding to ovs ports."""
|
|
||||||
__tablename__ = 'port_binding'
|
|
||||||
|
|
||||||
id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
|
||||||
network_id = sa.Column(sa.String(255), sa.ForeignKey("networks.id"),
|
|
||||||
nullable=False)
|
|
||||||
port_id = sa.Column(sa.String(255), sa.ForeignKey("ports.id"), unique=True,
|
|
||||||
nullable=False)
|
|
||||||
dpid = sa.Column(sa.String(255), nullable=False)
|
|
||||||
port_no = sa.Column(sa.Integer, nullable=False)
|
|
||||||
|
|
||||||
def __init__(self, network_id, port_id, dpid, port_no):
|
|
||||||
self.network_id = network_id
|
|
||||||
self.port_id = port_id
|
|
||||||
self.dpid = dpid
|
|
||||||
self.port_no = port_no
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return "<PortBinding(%s,%s,%s,%s,%s)>" % (self.network_id,
|
|
||||||
self.port_id,
|
|
||||||
self.dpid,
|
|
||||||
self.port_no)
|
|
||||||
|
@ -17,10 +17,7 @@
|
|||||||
# @author: Isaku Yamahata
|
# @author: Isaku Yamahata
|
||||||
|
|
||||||
from ryu.app import client
|
from ryu.app import client
|
||||||
from ryu.app.client import ignore_http_not_found
|
|
||||||
from ryu.app import rest_nw_id
|
from ryu.app import rest_nw_id
|
||||||
from sqlalchemy.exc import IntegrityError
|
|
||||||
from sqlalchemy.orm import exc as orm_exc
|
|
||||||
|
|
||||||
from quantum.common import constants as q_const
|
from quantum.common import constants as q_const
|
||||||
from quantum.common import exceptions as q_exc
|
from quantum.common import exceptions as q_exc
|
||||||
@ -68,6 +65,7 @@ class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
|
|
||||||
self.client = client.OFPClient(ofp_api_host)
|
self.client = client.OFPClient(ofp_api_host)
|
||||||
self.tun_client = client.TunnelClient(ofp_api_host)
|
self.tun_client = client.TunnelClient(ofp_api_host)
|
||||||
|
self.iface_client = client.QuantumIfaceClient(ofp_api_host)
|
||||||
for nw_id in rest_nw_id.RESERVED_NETWORK_IDS:
|
for nw_id in rest_nw_id.RESERVED_NETWORK_IDS:
|
||||||
if nw_id != rest_nw_id.NW_ID_UNKNOWN:
|
if nw_id != rest_nw_id.NW_ID_UNKNOWN:
|
||||||
self.client.update_network(nw_id)
|
self.client.update_network(nw_id)
|
||||||
@ -89,20 +87,8 @@ class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
for tun in self.tunnel_key.all_list():
|
for tun in self.tunnel_key.all_list():
|
||||||
self.tun_client.update_tunnel_key(tun.network_id, tun.tunnel_key)
|
self.tun_client.update_tunnel_key(tun.network_id, tun.tunnel_key)
|
||||||
session = db.get_session()
|
session = db.get_session()
|
||||||
for port_binding in db_api_v2.port_binding_all_list(session):
|
for port in session.query(models_v2.Port).all():
|
||||||
network_id = port_binding.network_id
|
self.iface_client.update_network_id(port.id, port.network_id)
|
||||||
dpid = port_binding.dpid
|
|
||||||
port_no = port_binding.port_no
|
|
||||||
try:
|
|
||||||
port = session.query(models_v2.Port).filter(
|
|
||||||
models_v2.Port.id == port_binding.port_id).one()
|
|
||||||
except orm_exc.NoResultFound:
|
|
||||||
continue
|
|
||||||
except orm_exc.MultipleResultsFound:
|
|
||||||
continue
|
|
||||||
|
|
||||||
self.client.update_port(network_id, dpid, port_no)
|
|
||||||
self.client.update_mac(network_id, dpid, port_no, port.mac_address)
|
|
||||||
|
|
||||||
def _client_create_network(self, net_id, tunnel_key):
|
def _client_create_network(self, net_id, tunnel_key):
|
||||||
self.client.create_network(net_id)
|
self.client.create_network(net_id)
|
||||||
@ -161,21 +147,12 @@ class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
|
|
||||||
return [self._fields(net, fields) for net in nets]
|
return [self._fields(net, fields) for net in nets]
|
||||||
|
|
||||||
def delete_port(self, context, id, l3_port_check=True):
|
def create_port(self, context, port):
|
||||||
with context.session.begin(subtransactions=True):
|
port = super(RyuQuantumPluginV2, self).create_port(context, port)
|
||||||
port = self._get_port(context, id)
|
self.iface_client.create_network_id(port['id'], port['network_id'])
|
||||||
net_id = port.network_id
|
return port
|
||||||
try:
|
|
||||||
port_binding = db_api_v2.port_binding_destroy(context.session,
|
|
||||||
port.id, net_id)
|
|
||||||
datapath_id = port_binding.dpid
|
|
||||||
port_no = port_binding.port_no
|
|
||||||
ignore_http_not_found(
|
|
||||||
lambda: self.client.delete_port(net_id, datapath_id,
|
|
||||||
port_no))
|
|
||||||
except q_exc.PortNotFound:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
def delete_port(self, context, id, l3_port_check=True):
|
||||||
# if needed, check to see if this is a port owned by
|
# if needed, check to see if this is a port owned by
|
||||||
# and l3-router. If so, we should prevent deletion.
|
# and l3-router. If so, we should prevent deletion.
|
||||||
if l3_port_check:
|
if l3_port_check:
|
||||||
@ -184,52 +161,9 @@ class RyuQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
return super(RyuQuantumPluginV2, self).delete_port(context, id)
|
return super(RyuQuantumPluginV2, self).delete_port(context, id)
|
||||||
|
|
||||||
def update_port(self, context, id, port):
|
def update_port(self, context, id, port):
|
||||||
p = super(RyuQuantumPluginV2, self).update_port(context, id, port)
|
|
||||||
net_id = p['network_id']
|
|
||||||
mac_address = p['mac_address']
|
|
||||||
|
|
||||||
deleted = port['port'].get('deleted', False)
|
deleted = port['port'].get('deleted', False)
|
||||||
|
port = super(RyuQuantumPluginV2, self).update_port(context, id, port)
|
||||||
if deleted:
|
if deleted:
|
||||||
session = context.session
|
session = context.session
|
||||||
try:
|
|
||||||
db_api_v2.port_binding_destroy(session, id, net_id)
|
|
||||||
except q_exc.PortNotFound:
|
|
||||||
pass
|
|
||||||
db_api_v2.set_port_status(session, id, q_const.PORT_STATUS_DOWN)
|
db_api_v2.set_port_status(session, id, q_const.PORT_STATUS_DOWN)
|
||||||
return p
|
return port
|
||||||
|
|
||||||
datapath_id = port['port'].get('datapath_id', None)
|
|
||||||
port_no = port['port'].get('port_no', None)
|
|
||||||
if datapath_id is None or port_no is None:
|
|
||||||
LOG.debug('p %s', p)
|
|
||||||
return p
|
|
||||||
|
|
||||||
try:
|
|
||||||
port_binding = db_api_v2.port_binding_get(id, net_id)
|
|
||||||
except orm_exc.NoResultFound:
|
|
||||||
try:
|
|
||||||
db_api_v2.port_binding_create(id, net_id, datapath_id, port_no)
|
|
||||||
except IntegrityError:
|
|
||||||
# TODO:XXX should do transaction?
|
|
||||||
return p
|
|
||||||
else:
|
|
||||||
self.client.create_port(net_id, datapath_id, port_no)
|
|
||||||
self.client.create_mac(net_id, datapath_id, port_no,
|
|
||||||
mac_address)
|
|
||||||
else:
|
|
||||||
if (port_binding.dpid != datapath_id or
|
|
||||||
port_binding.port_no != port_no):
|
|
||||||
variables = {'datapath_id': datapath_id,
|
|
||||||
'port_no': port_no,
|
|
||||||
'port_binding_dpid': port_binding.dpid,
|
|
||||||
'port_binding_port_no': port_binding.port_no}
|
|
||||||
raise q_exc.InvalidInput(
|
|
||||||
error_message=_('invalid (datapath_id, port_no) '
|
|
||||||
'is requested'
|
|
||||||
'(%(datapath_id)s, %(port_no)s), acutal'
|
|
||||||
'(%(port_binding_dpid)s, '
|
|
||||||
'%(port_binding_port_no)s)') % variables)
|
|
||||||
self.client.update_network(net_id)
|
|
||||||
self.client.update_port(net_id, datapath_id, port_no)
|
|
||||||
self.client.update_mac(net_id, datapath_id, port_no, mac_address)
|
|
||||||
return p
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user