Refactor BigSwitch error handling to use db rollbacks
This patch adjusts most of the logic handling the calls to the controller in the BigSwitch/floodlight plugin to make use of the db rollbacks from sqlalchemy for free on exceptions. This eliminates several complex try-except blocks and makes maintaining db<->controller consistency easier. Fixes: bug #1215823 Change-Id: Ia636c40e744b3b1c543e891791bf492df4f675d2
This commit is contained in:
parent
09dad2f1fe
commit
f3215f8d90
@ -68,6 +68,7 @@ from neutron.db import l3_db
|
|||||||
from neutron.extensions import extra_dhcp_opt as edo_ext
|
from neutron.extensions import extra_dhcp_opt as edo_ext
|
||||||
from neutron.extensions import l3
|
from neutron.extensions import l3
|
||||||
from neutron.extensions import portbindings
|
from neutron.extensions import portbindings
|
||||||
|
from neutron.openstack.common import excutils
|
||||||
from neutron.openstack.common import log as logging
|
from neutron.openstack.common import log as logging
|
||||||
from neutron.openstack.common import rpc
|
from neutron.openstack.common import rpc
|
||||||
from neutron.plugins.bigswitch.db import porttracker_db
|
from neutron.plugins.bigswitch.db import porttracker_db
|
||||||
@ -302,6 +303,10 @@ class ServerPool(object):
|
|||||||
'server': (active_server.server,
|
'server': (active_server.server,
|
||||||
active_server.port),
|
active_server.port),
|
||||||
'response': ret[3]})
|
'response': ret[3]})
|
||||||
|
LOG.error(_("ServerProxy: Error details: status=%(status)d, "
|
||||||
|
"reason=%(reason)r, ret=%(ret)s, data=%(data)r"),
|
||||||
|
{'status': ret[0], 'reason': ret[1], 'ret': ret[2],
|
||||||
|
'data': ret[3]})
|
||||||
active_server.failed = True
|
active_server.failed = True
|
||||||
|
|
||||||
# All servers failed, reset server list and try again next time
|
# All servers failed, reset server list and try again next time
|
||||||
@ -312,17 +317,106 @@ class ServerPool(object):
|
|||||||
s.port) for s in self.servers)})
|
s.port) for s in self.servers)})
|
||||||
return (0, None, None, None)
|
return (0, None, None, None)
|
||||||
|
|
||||||
def get(self, resource, data='', headers=None, ignore_codes=[]):
|
def rest_action(self, action, resource, data='', errstr='%s',
|
||||||
return self.rest_call('GET', resource, data, headers, ignore_codes)
|
ignore_codes=[], headers=None):
|
||||||
|
"""
|
||||||
|
Wrapper for rest_call that verifies success and raises a
|
||||||
|
RemoteRestError on failure with a provided error string
|
||||||
|
By default, 404 errors on DELETE calls are ignored because
|
||||||
|
they already do not exist on the backend.
|
||||||
|
"""
|
||||||
|
if not ignore_codes and action == 'DELETE':
|
||||||
|
ignore_codes = [404]
|
||||||
|
resp = self.rest_call(action, resource, data, headers, ignore_codes)
|
||||||
|
if self.server_failure(resp, ignore_codes):
|
||||||
|
LOG.error(_("NeutronRestProxyV2: ") + errstr, resp[2])
|
||||||
|
raise RemoteRestError(resp[2])
|
||||||
|
if resp[0] in ignore_codes:
|
||||||
|
LOG.warning(_("NeutronRestProxyV2: Received and ignored error "
|
||||||
|
"code %(code)d on %(action)s action to resource "
|
||||||
|
"%(resource)s"),
|
||||||
|
{'code': resp[2], 'action': action,
|
||||||
|
'resource': resource})
|
||||||
|
return resp
|
||||||
|
|
||||||
def put(self, resource, data, headers=None, ignore_codes=[]):
|
def rest_create_router(self, tenant_id, router):
|
||||||
return self.rest_call('PUT', resource, data, headers, ignore_codes)
|
resource = ROUTER_RESOURCE_PATH % tenant_id
|
||||||
|
data = {"router": router}
|
||||||
|
errstr = _("Unable to create remote router: %s")
|
||||||
|
self.rest_action('POST', resource, data, errstr)
|
||||||
|
|
||||||
def post(self, resource, data, headers=None, ignore_codes=[]):
|
def rest_update_router(self, tenant_id, router, router_id):
|
||||||
return self.rest_call('POST', resource, data, headers, ignore_codes)
|
resource = ROUTERS_PATH % (tenant_id, router_id)
|
||||||
|
data = {"router": router}
|
||||||
|
errstr = _("Unable to update remote router: %s")
|
||||||
|
self.rest_action('PUT', resource, data, errstr)
|
||||||
|
|
||||||
def delete(self, resource, data='', headers=None, ignore_codes=[]):
|
def rest_delete_router(self, tenant_id, router_id):
|
||||||
return self.rest_call('DELETE', resource, data, headers, ignore_codes)
|
resource = ROUTERS_PATH % (tenant_id, router_id)
|
||||||
|
errstr = _("Unable to delete remote router: %s")
|
||||||
|
self.rest_action('DELETE', resource, errstr=errstr)
|
||||||
|
|
||||||
|
def rest_add_router_interface(self, tenant_id, router_id, intf_details):
|
||||||
|
resource = ROUTER_INTF_OP_PATH % (tenant_id, router_id)
|
||||||
|
data = {"interface": intf_details}
|
||||||
|
errstr = _("Unable to add router interface: %s")
|
||||||
|
self.rest_action('POST', resource, data, errstr)
|
||||||
|
|
||||||
|
def rest_remove_router_interface(self, tenant_id, router_id, interface_id):
|
||||||
|
resource = ROUTER_INTF_PATH % (tenant_id, router_id, interface_id)
|
||||||
|
errstr = _("Unable to delete remote intf: %s")
|
||||||
|
self.rest_action('DELETE', resource, errstr=errstr)
|
||||||
|
|
||||||
|
def rest_create_network(self, tenant_id, network):
|
||||||
|
resource = NET_RESOURCE_PATH % tenant_id
|
||||||
|
data = {"network": network}
|
||||||
|
errstr = _("Unable to create remote network: %s")
|
||||||
|
self.rest_action('POST', resource, data, errstr)
|
||||||
|
|
||||||
|
def rest_update_network(self, tenant_id, net_id, network):
|
||||||
|
resource = NETWORKS_PATH % (tenant_id, net_id)
|
||||||
|
data = {"network": network}
|
||||||
|
errstr = _("Unable to update remote network: %s")
|
||||||
|
self.rest_action('PUT', resource, data, errstr)
|
||||||
|
|
||||||
|
def rest_delete_network(self, tenant_id, net_id):
|
||||||
|
resource = NETWORKS_PATH % (tenant_id, net_id)
|
||||||
|
errstr = _("Unable to update remote network: %s")
|
||||||
|
self.rest_action('DELETE', resource, errstr=errstr)
|
||||||
|
|
||||||
|
def rest_create_port(self, net, port):
|
||||||
|
resource = PORT_RESOURCE_PATH % (net["tenant_id"], net["id"])
|
||||||
|
data = {"port": port}
|
||||||
|
errstr = _("Unable to create remote port: %s")
|
||||||
|
self.rest_action('POST', resource, data, errstr)
|
||||||
|
|
||||||
|
def rest_update_port(self, tenant_id, network_id, port, port_id):
|
||||||
|
resource = PORTS_PATH % (tenant_id, network_id, port_id)
|
||||||
|
data = {"port": port}
|
||||||
|
errstr = _("Unable to update remote port: %s")
|
||||||
|
self.rest_action('PUT', resource, data, errstr)
|
||||||
|
|
||||||
|
def rest_delete_port(self, tenant_id, network_id, port_id):
|
||||||
|
resource = PORTS_PATH % (tenant_id, network_id, port_id)
|
||||||
|
errstr = _("Unable to delete remote port: %s")
|
||||||
|
self.rest_action('DELETE', resource, errstr=errstr)
|
||||||
|
|
||||||
|
def rest_plug_interface(self, tenant_id, net_id, port,
|
||||||
|
remote_interface_id):
|
||||||
|
if port["mac_address"] is not None:
|
||||||
|
resource = ATTACHMENT_PATH % (tenant_id, net_id, port["id"])
|
||||||
|
data = {"attachment":
|
||||||
|
{"id": remote_interface_id,
|
||||||
|
"mac": port["mac_address"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errstr = _("Unable to plug in interface: %s")
|
||||||
|
self.rest_action('PUT', resource, data, errstr)
|
||||||
|
|
||||||
|
def rest_unplug_interface(self, tenant_id, net_id, port_id):
|
||||||
|
resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id)
|
||||||
|
errstr = _("Unable to unplug interface: %s")
|
||||||
|
self.rest_action('DELETE', resource, errstr=errstr)
|
||||||
|
|
||||||
|
|
||||||
class RpcProxy(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
class RpcProxy(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
||||||
@ -413,32 +507,20 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
self._warn_on_state_status(network['network'])
|
self._warn_on_state_status(network['network'])
|
||||||
|
|
||||||
# Validate args
|
with context.session.begin(subtransactions=True):
|
||||||
tenant_id = self._get_tenant_id_for_create(context, network["network"])
|
# Validate args
|
||||||
|
tenant_id = self._get_tenant_id_for_create(context,
|
||||||
|
network["network"])
|
||||||
|
|
||||||
session = context.session
|
|
||||||
with session.begin(subtransactions=True):
|
|
||||||
# create network in DB
|
# create network in DB
|
||||||
new_net = super(NeutronRestProxyV2, self).create_network(context,
|
new_net = super(NeutronRestProxyV2, self).create_network(context,
|
||||||
network)
|
network)
|
||||||
self._process_l3_create(context, new_net, network['network'])
|
self._process_l3_create(context, new_net, network['network'])
|
||||||
|
mapped_network = self._get_mapped_network_with_subnets(new_net,
|
||||||
|
context)
|
||||||
|
|
||||||
# create network on the network controller
|
# create network on the network controller
|
||||||
try:
|
self.servers.rest_create_network(tenant_id, mapped_network)
|
||||||
resource = NET_RESOURCE_PATH % tenant_id
|
|
||||||
mapped_network = self._get_mapped_network_with_subnets(new_net)
|
|
||||||
data = {
|
|
||||||
"network": mapped_network
|
|
||||||
}
|
|
||||||
ret = self.servers.post(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2:Unable to create remote "
|
|
||||||
"network: %s"), e.message)
|
|
||||||
super(NeutronRestProxyV2, self).delete_network(context,
|
|
||||||
new_net['id'])
|
|
||||||
raise
|
|
||||||
|
|
||||||
# return created network
|
# return created network
|
||||||
return new_net
|
return new_net
|
||||||
@ -472,25 +554,12 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
session = context.session
|
session = context.session
|
||||||
with session.begin(subtransactions=True):
|
with session.begin(subtransactions=True):
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
new_net = super(NeutronRestProxyV2, self).update_network(
|
||||||
net_id)
|
context, net_id, network)
|
||||||
new_net = super(NeutronRestProxyV2, self).update_network(context,
|
|
||||||
net_id,
|
|
||||||
network)
|
|
||||||
self._process_l3_update(context, new_net, network['network'])
|
self._process_l3_update(context, new_net, network['network'])
|
||||||
|
|
||||||
# update network on network controller
|
# update network on network controller
|
||||||
try:
|
self._send_update_network(new_net, context)
|
||||||
self._send_update_network(new_net)
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to update remote "
|
|
||||||
"network: %s"), e.message)
|
|
||||||
# reset network to original state
|
|
||||||
super(NeutronRestProxyV2, self).update_network(context, id,
|
|
||||||
orig_net)
|
|
||||||
raise
|
|
||||||
|
|
||||||
# return updated network
|
|
||||||
return new_net
|
return new_net
|
||||||
|
|
||||||
def delete_network(self, context, net_id):
|
def delete_network(self, context, net_id):
|
||||||
@ -520,20 +589,11 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
if not only_auto_del:
|
if not only_auto_del:
|
||||||
raise exceptions.NetworkInUse(net_id=net_id)
|
raise exceptions.NetworkInUse(net_id=net_id)
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
# delete from network ctrl. Remote error on delete is ignored
|
|
||||||
try:
|
|
||||||
resource = NETWORKS_PATH % (tenant_id, net_id)
|
|
||||||
ret = self.servers.delete(resource)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
ret_val = super(NeutronRestProxyV2, self).delete_network(context,
|
ret_val = super(NeutronRestProxyV2, self).delete_network(context,
|
||||||
net_id)
|
net_id)
|
||||||
|
self.servers.rest_delete_network(tenant_id, net_id)
|
||||||
return ret_val
|
return ret_val
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to update remote "
|
|
||||||
"network: %s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_port(self, context, port):
|
def create_port(self, context, port):
|
||||||
"""Create a port, which is a connection point of a device
|
"""Create a port, which is a connection point of a device
|
||||||
@ -563,48 +623,46 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
"""
|
"""
|
||||||
LOG.debug(_("NeutronRestProxyV2: create_port() called"))
|
LOG.debug(_("NeutronRestProxyV2: create_port() called"))
|
||||||
|
|
||||||
# Update DB
|
# Update DB in new session so exceptions rollback changes
|
||||||
port["port"]["admin_state_up"] = False
|
with context.session.begin(subtransactions=True):
|
||||||
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
|
port["port"]["admin_state_up"] = False
|
||||||
new_port = super(NeutronRestProxyV2, self).create_port(context, port)
|
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
|
||||||
if (portbindings.HOST_ID in port['port']
|
new_port = super(NeutronRestProxyV2, self).create_port(context,
|
||||||
and 'id' in new_port):
|
port)
|
||||||
porttracker_db.put_port_hostid(context, new_port['id'],
|
if (portbindings.HOST_ID in port['port']
|
||||||
port['port'][portbindings.HOST_ID])
|
and 'id' in new_port):
|
||||||
self._process_port_create_extra_dhcp_opts(context, new_port, dhcp_opts)
|
host_id = port['port'][portbindings.HOST_ID]
|
||||||
new_port = self._extend_port_dict_binding(context, new_port)
|
porttracker_db.put_port_hostid(context, new_port['id'],
|
||||||
net = super(NeutronRestProxyV2,
|
host_id)
|
||||||
self).get_network(context, new_port["network_id"])
|
self._process_port_create_extra_dhcp_opts(context, new_port,
|
||||||
|
dhcp_opts)
|
||||||
|
new_port = self._extend_port_dict_binding(context, new_port)
|
||||||
|
net = super(NeutronRestProxyV2,
|
||||||
|
self).get_network(context, new_port["network_id"])
|
||||||
|
|
||||||
if self.add_meta_server_route:
|
if self.add_meta_server_route:
|
||||||
if new_port['device_owner'] == 'network:dhcp':
|
if new_port['device_owner'] == 'network:dhcp':
|
||||||
destination = METADATA_SERVER_IP + '/32'
|
destination = METADATA_SERVER_IP + '/32'
|
||||||
self._add_host_route(context, destination, new_port)
|
self._add_host_route(context, destination, new_port)
|
||||||
|
|
||||||
# create on networl ctrl
|
# create on network ctrl
|
||||||
try:
|
|
||||||
resource = PORT_RESOURCE_PATH % (net["tenant_id"], net["id"])
|
|
||||||
mapped_port = self._map_state_and_status(new_port)
|
mapped_port = self._map_state_and_status(new_port)
|
||||||
data = {
|
self.servers.rest_create_port(net, mapped_port)
|
||||||
"port": mapped_port
|
|
||||||
}
|
|
||||||
ret = self.servers.post(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
|
|
||||||
# connect device to network, if present
|
|
||||||
device_id = port["port"].get("device_id")
|
|
||||||
if device_id:
|
|
||||||
self._plug_interface(context,
|
|
||||||
net["tenant_id"], net["id"],
|
|
||||||
new_port["id"], device_id)
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to create remote port: "
|
|
||||||
"%s"), e.message)
|
|
||||||
super(NeutronRestProxyV2, self).delete_port(context,
|
|
||||||
new_port["id"])
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
# connect device to network, if present
|
||||||
|
device_id = port["port"].get("device_id")
|
||||||
|
if device_id:
|
||||||
|
try:
|
||||||
|
self.servers.rest_plug_interface(net["tenant_id"], net["id"],
|
||||||
|
new_port, device_id)
|
||||||
|
except RemoteRestError:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
port_update = {"port": {"status": "ERROR"}}
|
||||||
|
super(NeutronRestProxyV2, self).update_port(
|
||||||
|
context,
|
||||||
|
new_port["id"],
|
||||||
|
port_update
|
||||||
|
)
|
||||||
# Set port state up and return that port
|
# Set port state up and return that port
|
||||||
port_update = {"port": {"admin_state_up": True}}
|
port_update = {"port": {"admin_state_up": True}}
|
||||||
new_port = super(NeutronRestProxyV2, self).update_port(context,
|
new_port = super(NeutronRestProxyV2, self).update_port(context,
|
||||||
@ -660,44 +718,45 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
# Validate Args
|
# Validate Args
|
||||||
orig_port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
orig_port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
# Update DB
|
||||||
|
new_port = super(NeutronRestProxyV2,
|
||||||
|
self).update_port(context, port_id, port)
|
||||||
|
self._update_extra_dhcp_opts_on_port(context, port_id, port,
|
||||||
|
new_port)
|
||||||
|
if (portbindings.HOST_ID in port['port']
|
||||||
|
and 'id' in new_port):
|
||||||
|
host_id = port['port'][portbindings.HOST_ID]
|
||||||
|
porttracker_db.put_port_hostid(context, new_port['id'],
|
||||||
|
host_id)
|
||||||
|
new_port = self._extend_port_dict_binding(context, new_port)
|
||||||
|
|
||||||
# Update DB
|
# update on networl ctrl
|
||||||
new_port = super(NeutronRestProxyV2, self).update_port(context,
|
|
||||||
port_id, port)
|
|
||||||
self._update_extra_dhcp_opts_on_port(context, port_id, port, new_port)
|
|
||||||
if (portbindings.HOST_ID in port['port']
|
|
||||||
and 'id' in new_port):
|
|
||||||
porttracker_db.put_port_hostid(context, new_port['id'],
|
|
||||||
port['port'][portbindings.HOST_ID])
|
|
||||||
new_port = self._extend_port_dict_binding(context, new_port)
|
|
||||||
# update on networl ctrl
|
|
||||||
try:
|
|
||||||
resource = PORTS_PATH % (orig_port["tenant_id"],
|
|
||||||
orig_port["network_id"], port_id)
|
|
||||||
mapped_port = self._map_state_and_status(new_port)
|
mapped_port = self._map_state_and_status(new_port)
|
||||||
data = {"port": mapped_port}
|
self.servers.rest_update_port(orig_port["tenant_id"],
|
||||||
ret = self.servers.put(resource, data)
|
orig_port["network_id"],
|
||||||
if not self.servers.action_success(ret):
|
mapped_port, port_id)
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
|
|
||||||
if new_port.get("device_id") != orig_port.get("device_id"):
|
if (new_port.get("device_id") != orig_port.get("device_id") and
|
||||||
if orig_port.get("device_id"):
|
orig_port.get("device_id")):
|
||||||
self._unplug_interface(context, orig_port["tenant_id"],
|
try:
|
||||||
orig_port["network_id"],
|
self.servers.rest_unplug_interface(orig_port["tenant_id"],
|
||||||
orig_port["id"])
|
orig_port["network_id"],
|
||||||
|
orig_port["id"])
|
||||||
device_id = new_port.get("device_id")
|
device_id = new_port.get("device_id")
|
||||||
if device_id:
|
if device_id:
|
||||||
self._plug_interface(context, new_port["tenant_id"],
|
self.rest_plug_interface(new_port["tenant_id"],
|
||||||
new_port["network_id"],
|
new_port["network_id"],
|
||||||
new_port["id"], device_id)
|
new_port, device_id)
|
||||||
|
|
||||||
except RemoteRestError as e:
|
except RemoteRestError:
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to create remote port: "
|
with excutils.save_and_reraise_exception():
|
||||||
"%s"), e.message)
|
port_update = {"port": {"status": "ERROR"}}
|
||||||
# reset port to original state
|
super(NeutronRestProxyV2, self).update_port(
|
||||||
super(NeutronRestProxyV2, self).update_port(context, port_id,
|
context,
|
||||||
orig_port)
|
new_port["id"],
|
||||||
raise
|
port_update
|
||||||
|
)
|
||||||
|
|
||||||
# return new_port
|
# return new_port
|
||||||
return new_port
|
return new_port
|
||||||
@ -719,115 +778,57 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
# 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:
|
||||||
self.prevent_l3_port_deletion(context, port_id)
|
self.prevent_l3_port_deletion(context, port_id)
|
||||||
self.disassociate_floatingips(context, port_id)
|
with context.session.begin(subtransactions=True):
|
||||||
|
self.disassociate_floatingips(context, port_id)
|
||||||
|
self._unplug_port(context, port_id)
|
||||||
|
# Separate transaction for delete in case unplug passes
|
||||||
|
# but delete fails on controller
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
super(NeutronRestProxyV2, self).delete_port(context, port_id)
|
||||||
|
|
||||||
super(NeutronRestProxyV2, self).delete_port(context, port_id)
|
def _unplug_port(self, context, port_id):
|
||||||
|
|
||||||
def _delete_port(self, context, port_id):
|
|
||||||
# Delete from DB
|
|
||||||
port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
||||||
tenant_id = port['tenant_id']
|
tenant_id = port['tenant_id']
|
||||||
|
net_id = port['network_id']
|
||||||
if tenant_id == '':
|
if tenant_id == '':
|
||||||
net = super(NeutronRestProxyV2,
|
net = super(NeutronRestProxyV2, self).get_network(context, net_id)
|
||||||
self).get_network(context, port['network_id'])
|
|
||||||
tenant_id = net['tenant_id']
|
tenant_id = net['tenant_id']
|
||||||
|
if port.get("device_id"):
|
||||||
|
self.servers.rest_unplug_interface(tenant_id, net_id, port_id)
|
||||||
|
# Port should transition to error state now that it's unplugged
|
||||||
|
# but not yet deleted
|
||||||
|
port_update = {"port": {"status": "ERROR"}}
|
||||||
|
super(NeutronRestProxyV2, self).update_port(context,
|
||||||
|
port_id,
|
||||||
|
port_update)
|
||||||
|
|
||||||
# delete from network ctrl. Remote error on delete is ignored
|
def _delete_port(self, context, port_id):
|
||||||
try:
|
port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
||||||
resource = PORTS_PATH % (tenant_id, port["network_id"],
|
tenant_id = port['tenant_id']
|
||||||
port_id)
|
net_id = port['network_id']
|
||||||
ret = self.servers.delete(resource)
|
if tenant_id == '':
|
||||||
if not self.servers.action_success(ret):
|
net = super(NeutronRestProxyV2, self).get_network(context, net_id)
|
||||||
raise RemoteRestError(ret[2])
|
tenant_id = net['tenant_id']
|
||||||
|
# Delete from DB
|
||||||
if port.get("device_id"):
|
ret_val = super(NeutronRestProxyV2,
|
||||||
self._unplug_interface(context, tenant_id,
|
self)._delete_port(context, port_id)
|
||||||
port["network_id"], port["id"])
|
self.servers.rest_delete_port(tenant_id, net_id, port_id)
|
||||||
ret_val = super(NeutronRestProxyV2, self)._delete_port(context,
|
return ret_val
|
||||||
port_id)
|
|
||||||
return ret_val
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to update remote port: "
|
|
||||||
"%s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _plug_interface(self, context, tenant_id, net_id, port_id,
|
|
||||||
remote_interface_id):
|
|
||||||
"""Plug remote interface to the network.
|
|
||||||
|
|
||||||
Attaches a remote interface to the specified port on the specified
|
|
||||||
Virtual Network.
|
|
||||||
|
|
||||||
:returns: None
|
|
||||||
|
|
||||||
:raises: exceptions.NetworkNotFound
|
|
||||||
:raises: exceptions.PortNotFound
|
|
||||||
:raises: RemoteRestError
|
|
||||||
"""
|
|
||||||
LOG.debug(_("NeutronRestProxyV2: _plug_interface() called"))
|
|
||||||
|
|
||||||
# update attachment on network controller
|
|
||||||
try:
|
|
||||||
port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
|
||||||
mac = port["mac_address"]
|
|
||||||
|
|
||||||
if mac is not None:
|
|
||||||
resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id)
|
|
||||||
data = {"attachment":
|
|
||||||
{"id": remote_interface_id,
|
|
||||||
"mac": mac,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ret = self.servers.put(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2:Unable to update remote network: "
|
|
||||||
"%s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _unplug_interface(self, context, tenant_id, net_id, port_id):
|
|
||||||
"""Detach interface from the network controller.
|
|
||||||
|
|
||||||
Detaches a remote interface from the specified port on the network
|
|
||||||
controller.
|
|
||||||
|
|
||||||
:returns: None
|
|
||||||
|
|
||||||
:raises: RemoteRestError
|
|
||||||
"""
|
|
||||||
LOG.debug(_("NeutronRestProxyV2: _unplug_interface() called"))
|
|
||||||
|
|
||||||
# delete from network ctrl. Remote error on delete is ignored
|
|
||||||
try:
|
|
||||||
resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id)
|
|
||||||
ret = self.servers.delete(resource, ignore_codes=[404])
|
|
||||||
if self.servers.server_failure(ret, ignore_codes=[404]):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to update remote port: "
|
|
||||||
"%s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def create_subnet(self, context, subnet):
|
def create_subnet(self, context, subnet):
|
||||||
LOG.debug(_("NeutronRestProxyV2: create_subnet() called"))
|
LOG.debug(_("NeutronRestProxyV2: create_subnet() called"))
|
||||||
|
|
||||||
self._warn_on_state_status(subnet['subnet'])
|
self._warn_on_state_status(subnet['subnet'])
|
||||||
|
|
||||||
# create subnet in DB
|
with context.session.begin(subtransactions=True):
|
||||||
new_subnet = super(NeutronRestProxyV2, self).create_subnet(context,
|
# create subnet in DB
|
||||||
subnet)
|
new_subnet = super(NeutronRestProxyV2,
|
||||||
net_id = new_subnet['network_id']
|
self).create_subnet(context, subnet)
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
net_id = new_subnet['network_id']
|
||||||
net_id)
|
orig_net = super(NeutronRestProxyV2,
|
||||||
# update network on network controller
|
self).get_network(context, net_id)
|
||||||
try:
|
# update network on network controller
|
||||||
self._send_update_network(orig_net)
|
self._send_update_network(orig_net)
|
||||||
except RemoteRestError:
|
|
||||||
# rollback creation of subnet
|
|
||||||
super(NeutronRestProxyV2, self).delete_subnet(context,
|
|
||||||
subnet['id'])
|
|
||||||
raise
|
|
||||||
return new_subnet
|
return new_subnet
|
||||||
|
|
||||||
def update_subnet(self, context, id, subnet):
|
def update_subnet(self, context, id, subnet):
|
||||||
@ -835,38 +836,28 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
self._warn_on_state_status(subnet['subnet'])
|
self._warn_on_state_status(subnet['subnet'])
|
||||||
|
|
||||||
orig_subnet = super(NeutronRestProxyV2, self)._get_subnet(context, id)
|
with context.session.begin(subtransactions=True):
|
||||||
|
# update subnet in DB
|
||||||
# update subnet in DB
|
new_subnet = super(NeutronRestProxyV2,
|
||||||
new_subnet = super(NeutronRestProxyV2, self).update_subnet(context, id,
|
self).update_subnet(context, id, subnet)
|
||||||
subnet)
|
net_id = new_subnet['network_id']
|
||||||
net_id = new_subnet['network_id']
|
orig_net = super(NeutronRestProxyV2,
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
self).get_network(context, net_id)
|
||||||
net_id)
|
# update network on network controller
|
||||||
# update network on network controller
|
|
||||||
try:
|
|
||||||
self._send_update_network(orig_net)
|
self._send_update_network(orig_net)
|
||||||
except RemoteRestError:
|
return new_subnet
|
||||||
# rollback updation of subnet
|
|
||||||
super(NeutronRestProxyV2, self).update_subnet(context, id,
|
|
||||||
orig_subnet)
|
|
||||||
raise
|
|
||||||
return new_subnet
|
|
||||||
|
|
||||||
def delete_subnet(self, context, id):
|
def delete_subnet(self, context, id):
|
||||||
LOG.debug(_("NeutronRestProxyV2: delete_subnet() called"))
|
LOG.debug(_("NeutronRestProxyV2: delete_subnet() called"))
|
||||||
orig_subnet = super(NeutronRestProxyV2, self).get_subnet(context, id)
|
orig_subnet = super(NeutronRestProxyV2, self).get_subnet(context, id)
|
||||||
net_id = orig_subnet['network_id']
|
net_id = orig_subnet['network_id']
|
||||||
# delete subnet in DB
|
with context.session.begin(subtransactions=True):
|
||||||
super(NeutronRestProxyV2, self).delete_subnet(context, id)
|
# delete subnet in DB
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
super(NeutronRestProxyV2, self).delete_subnet(context, id)
|
||||||
net_id)
|
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||||
# update network on network controller
|
net_id)
|
||||||
try:
|
# update network on network controller - exception will rollback
|
||||||
self._send_update_network(orig_net)
|
self._send_update_network(orig_net)
|
||||||
except RemoteRestError:
|
|
||||||
# TODO(Sumit): rollback deletion of subnet
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _get_tenant_default_router_rules(self, tenant):
|
def _get_tenant_default_router_rules(self, tenant):
|
||||||
rules = cfg.CONF.ROUTER.tenant_default_router_rule
|
rules = cfg.CONF.ROUTER.tenant_default_router_rule
|
||||||
@ -905,29 +896,15 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
rules = self._get_tenant_default_router_rules(tenant_id)
|
rules = self._get_tenant_default_router_rules(tenant_id)
|
||||||
router['router']['router_rules'] = rules
|
router['router']['router_rules'] = rules
|
||||||
|
|
||||||
# create router in DB
|
with context.session.begin(subtransactions=True):
|
||||||
new_router = super(NeutronRestProxyV2, self).create_router(context,
|
# create router in DB
|
||||||
router)
|
new_router = super(NeutronRestProxyV2, self).create_router(context,
|
||||||
|
router)
|
||||||
# create router on the network controller
|
|
||||||
try:
|
|
||||||
resource = ROUTER_RESOURCE_PATH % tenant_id
|
|
||||||
mapped_router = self._map_state_and_status(new_router)
|
mapped_router = self._map_state_and_status(new_router)
|
||||||
data = {
|
self.servers.rest_create_router(tenant_id, mapped_router)
|
||||||
"router": mapped_router
|
|
||||||
}
|
|
||||||
ret = self.servers.post(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to create remote router: "
|
|
||||||
"%s"), e.message)
|
|
||||||
super(NeutronRestProxyV2, self).delete_router(context,
|
|
||||||
new_router['id'])
|
|
||||||
raise
|
|
||||||
|
|
||||||
# return created router
|
# return created router
|
||||||
return new_router
|
return new_router
|
||||||
|
|
||||||
def update_router(self, context, router_id, router):
|
def update_router(self, context, router_id, router):
|
||||||
|
|
||||||
@ -938,31 +915,16 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
orig_router = super(NeutronRestProxyV2, self).get_router(context,
|
orig_router = super(NeutronRestProxyV2, self).get_router(context,
|
||||||
router_id)
|
router_id)
|
||||||
tenant_id = orig_router["tenant_id"]
|
tenant_id = orig_router["tenant_id"]
|
||||||
new_router = super(NeutronRestProxyV2, self).update_router(context,
|
with context.session.begin(subtransactions=True):
|
||||||
router_id,
|
new_router = super(NeutronRestProxyV2,
|
||||||
router)
|
self).update_router(context, router_id, router)
|
||||||
|
router = self._map_state_and_status(new_router)
|
||||||
|
|
||||||
# update router on network controller
|
# update router on network controller
|
||||||
try:
|
self.servers.rest_update_router(tenant_id, router, router_id)
|
||||||
resource = ROUTERS_PATH % (tenant_id, router_id)
|
|
||||||
mapped_router = self._map_state_and_status(new_router)
|
|
||||||
data = {
|
|
||||||
"router": mapped_router
|
|
||||||
}
|
|
||||||
ret = self.servers.put(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to update remote router: "
|
|
||||||
"%s"), e.message)
|
|
||||||
# reset router to original state
|
|
||||||
super(NeutronRestProxyV2, self).update_router(context,
|
|
||||||
router_id,
|
|
||||||
orig_router)
|
|
||||||
raise
|
|
||||||
|
|
||||||
# return updated router
|
# return updated router
|
||||||
return new_router
|
return new_router
|
||||||
|
|
||||||
def delete_router(self, context, router_id):
|
def delete_router(self, context, router_id):
|
||||||
LOG.debug(_("NeutronRestProxyV2: delete_router() called"))
|
LOG.debug(_("NeutronRestProxyV2: delete_router() called"))
|
||||||
@ -985,20 +947,12 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
filters=device_filter)
|
filters=device_filter)
|
||||||
if ports:
|
if ports:
|
||||||
raise l3.RouterInUse(router_id=router_id)
|
raise l3.RouterInUse(router_id=router_id)
|
||||||
|
ret_val = super(NeutronRestProxyV2,
|
||||||
|
self).delete_router(context, router_id)
|
||||||
|
|
||||||
# delete from network ctrl. Remote error on delete is ignored
|
# delete from network ctrl
|
||||||
try:
|
self.servers.rest_delete_router(tenant_id, router_id)
|
||||||
resource = ROUTERS_PATH % (tenant_id, router_id)
|
|
||||||
ret = self.servers.delete(resource)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
ret_val = super(NeutronRestProxyV2, self).delete_router(context,
|
|
||||||
router_id)
|
|
||||||
return ret_val
|
return ret_val
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to delete remote router: "
|
|
||||||
"%s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def add_router_interface(self, context, router_id, interface_info):
|
def add_router_interface(self, context, router_id, interface_info):
|
||||||
|
|
||||||
@ -1008,36 +962,25 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
router = self._get_router(context, router_id)
|
router = self._get_router(context, router_id)
|
||||||
tenant_id = router['tenant_id']
|
tenant_id = router['tenant_id']
|
||||||
|
|
||||||
# create interface in DB
|
with context.session.begin(subtransactions=True):
|
||||||
new_interface_info = super(NeutronRestProxyV2,
|
# create interface in DB
|
||||||
self).add_router_interface(context,
|
new_intf_info = super(NeutronRestProxyV2,
|
||||||
router_id,
|
self).add_router_interface(context,
|
||||||
interface_info)
|
router_id,
|
||||||
port = self._get_port(context, new_interface_info['port_id'])
|
interface_info)
|
||||||
net_id = port['network_id']
|
port = self._get_port(context, new_intf_info['port_id'])
|
||||||
subnet_id = new_interface_info['subnet_id']
|
net_id = port['network_id']
|
||||||
# we will use the port's network id as interface's id
|
subnet_id = new_intf_info['subnet_id']
|
||||||
interface_id = net_id
|
# we will use the port's network id as interface's id
|
||||||
intf_details = self._get_router_intf_details(context,
|
interface_id = net_id
|
||||||
interface_id,
|
intf_details = self._get_router_intf_details(context,
|
||||||
subnet_id)
|
interface_id,
|
||||||
|
subnet_id)
|
||||||
|
|
||||||
# create interface on the network controller
|
# create interface on the network controller
|
||||||
try:
|
self.servers.rest_add_router_interface(tenant_id, router_id,
|
||||||
resource = ROUTER_INTF_OP_PATH % (tenant_id, router_id)
|
intf_details)
|
||||||
data = {"interface": intf_details}
|
return new_intf_info
|
||||||
ret = self.servers.post(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to create interface: "
|
|
||||||
"%s"), e.message)
|
|
||||||
super(NeutronRestProxyV2,
|
|
||||||
self).remove_router_interface(context, router_id,
|
|
||||||
interface_info)
|
|
||||||
raise
|
|
||||||
|
|
||||||
return new_interface_info
|
|
||||||
|
|
||||||
def remove_router_interface(self, context, router_id, interface_info):
|
def remove_router_interface(self, context, router_id, interface_info):
|
||||||
|
|
||||||
@ -1061,89 +1004,69 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
msg = "Either subnet_id or port_id must be specified"
|
msg = "Either subnet_id or port_id must be specified"
|
||||||
raise exceptions.BadRequest(resource='router', msg=msg)
|
raise exceptions.BadRequest(resource='router', msg=msg)
|
||||||
|
|
||||||
# remove router in DB
|
with context.session.begin(subtransactions=True):
|
||||||
del_intf_info = super(NeutronRestProxyV2,
|
# remove router in DB
|
||||||
self).remove_router_interface(context,
|
del_ret = super(NeutronRestProxyV2,
|
||||||
router_id,
|
self).remove_router_interface(context,
|
||||||
interface_info)
|
router_id,
|
||||||
|
interface_info)
|
||||||
|
|
||||||
# create router on the network controller
|
# create router on the network controller
|
||||||
try:
|
self.servers.rest_remove_router_interface(tenant_id, router_id,
|
||||||
resource = ROUTER_INTF_PATH % (tenant_id, router_id, interface_id)
|
interface_id)
|
||||||
ret = self.servers.delete(resource)
|
return del_ret
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2:Unable to delete remote intf: "
|
|
||||||
"%s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
# return new interface
|
|
||||||
return del_intf_info
|
|
||||||
|
|
||||||
def create_floatingip(self, context, floatingip):
|
def create_floatingip(self, context, floatingip):
|
||||||
LOG.debug(_("NeutronRestProxyV2: create_floatingip() called"))
|
LOG.debug(_("NeutronRestProxyV2: create_floatingip() called"))
|
||||||
|
|
||||||
# create floatingip in DB
|
with context.session.begin(subtransactions=True):
|
||||||
new_fl_ip = super(NeutronRestProxyV2,
|
# create floatingip in DB
|
||||||
self).create_floatingip(context, floatingip)
|
new_fl_ip = super(NeutronRestProxyV2,
|
||||||
|
self).create_floatingip(context, floatingip)
|
||||||
|
|
||||||
net_id = new_fl_ip['floating_network_id']
|
net_id = new_fl_ip['floating_network_id']
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||||
net_id)
|
net_id)
|
||||||
# create floatingip on the network controller
|
# create floatingip on the network controller
|
||||||
try:
|
try:
|
||||||
self._send_update_network(orig_net)
|
self._send_update_network(orig_net)
|
||||||
except RemoteRestError as e:
|
except RemoteRestError as e:
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to create remote "
|
with excutils.save_and_reraise_exception():
|
||||||
"floatin IP: %s"), e.message)
|
LOG.error(
|
||||||
super(NeutronRestProxyV2, self).delete_floatingip(context,
|
_("NeutronRestProxyV2: Unable to create remote "
|
||||||
floatingip)
|
"floating IP: %s"), e)
|
||||||
raise
|
# return created floating IP
|
||||||
|
return new_fl_ip
|
||||||
# return created floating IP
|
|
||||||
return new_fl_ip
|
|
||||||
|
|
||||||
def update_floatingip(self, context, id, floatingip):
|
def update_floatingip(self, context, id, floatingip):
|
||||||
LOG.debug(_("NeutronRestProxyV2: update_floatingip() called"))
|
LOG.debug(_("NeutronRestProxyV2: update_floatingip() called"))
|
||||||
|
|
||||||
orig_fl_ip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
with context.session.begin(subtransactions=True):
|
||||||
id)
|
# update floatingip in DB
|
||||||
|
new_fl_ip = super(NeutronRestProxyV2,
|
||||||
|
self).update_floatingip(context, id, floatingip)
|
||||||
|
|
||||||
# update floatingip in DB
|
net_id = new_fl_ip['floating_network_id']
|
||||||
new_fl_ip = super(NeutronRestProxyV2,
|
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||||
self).update_floatingip(context, id, floatingip)
|
net_id)
|
||||||
|
# update network on network controller
|
||||||
net_id = new_fl_ip['floating_network_id']
|
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
|
||||||
net_id)
|
|
||||||
# update network on network controller
|
|
||||||
try:
|
|
||||||
self._send_update_network(orig_net)
|
self._send_update_network(orig_net)
|
||||||
except RemoteRestError:
|
return new_fl_ip
|
||||||
# rollback updation of subnet
|
|
||||||
super(NeutronRestProxyV2, self).update_floatingip(context, id,
|
|
||||||
orig_fl_ip)
|
|
||||||
raise
|
|
||||||
return new_fl_ip
|
|
||||||
|
|
||||||
def delete_floatingip(self, context, id):
|
def delete_floatingip(self, context, id):
|
||||||
LOG.debug(_("NeutronRestProxyV2: delete_floatingip() called"))
|
LOG.debug(_("NeutronRestProxyV2: delete_floatingip() called"))
|
||||||
|
|
||||||
orig_fl_ip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
orig_fl_ip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
||||||
id)
|
id)
|
||||||
# delete floating IP in DB
|
with context.session.begin(subtransactions=True):
|
||||||
net_id = orig_fl_ip['floating_network_id']
|
# delete floating IP in DB
|
||||||
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
net_id = orig_fl_ip['floating_network_id']
|
||||||
|
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
||||||
|
|
||||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||||
net_id)
|
net_id)
|
||||||
# update network on network controller
|
# update network on network controller
|
||||||
try:
|
|
||||||
self._send_update_network(orig_net)
|
self._send_update_network(orig_net)
|
||||||
except RemoteRestError:
|
|
||||||
# TODO(Sumit): rollback deletion of floating IP
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _send_all_data(self):
|
def _send_all_data(self):
|
||||||
"""Pushes all data to network ctrl (networks/ports, ports/attachments).
|
"""Pushes all data to network ctrl (networks/ports, ports/attachments).
|
||||||
@ -1200,20 +1123,13 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
routers.append(mapped_router)
|
routers.append(mapped_router)
|
||||||
|
|
||||||
try:
|
resource = '/topology'
|
||||||
resource = '/topology'
|
data = {
|
||||||
data = {
|
'networks': networks,
|
||||||
'networks': networks,
|
'routers': routers,
|
||||||
'routers': routers,
|
}
|
||||||
}
|
errstr = _("Unable to update remote topology: %s")
|
||||||
ret = self.servers.put(resource, data)
|
return self.servers.rest_action('PUT', resource, data, errstr)
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
return ret
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_('NeutronRestProxy: Unable to update remote '
|
|
||||||
'topology: %s'), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _add_host_route(self, context, destination, port):
|
def _add_host_route(self, context, destination, port):
|
||||||
subnet = {}
|
subnet = {}
|
||||||
@ -1232,22 +1148,26 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
LOG.debug("destination:%s nexthop:%s" % (destination,
|
LOG.debug("destination:%s nexthop:%s" % (destination,
|
||||||
nexthop))
|
nexthop))
|
||||||
|
|
||||||
def _get_network_with_floatingips(self, network):
|
def _get_network_with_floatingips(self, network, context=None):
|
||||||
admin_context = qcontext.get_admin_context()
|
if context is None:
|
||||||
|
context = qcontext.get_admin_context()
|
||||||
|
|
||||||
net_id = network['id']
|
net_id = network['id']
|
||||||
net_filter = {'floating_network_id': [net_id]}
|
net_filter = {'floating_network_id': [net_id]}
|
||||||
fl_ips = super(NeutronRestProxyV2,
|
fl_ips = super(NeutronRestProxyV2,
|
||||||
self).get_floatingips(admin_context,
|
self).get_floatingips(context,
|
||||||
filters=net_filter) or []
|
filters=net_filter) or []
|
||||||
network['floatingips'] = fl_ips
|
network['floatingips'] = fl_ips
|
||||||
|
|
||||||
return network
|
return network
|
||||||
|
|
||||||
def _get_all_subnets_json_for_network(self, net_id):
|
def _get_all_subnets_json_for_network(self, net_id, context=None):
|
||||||
admin_context = qcontext.get_admin_context()
|
if context is None:
|
||||||
subnets = self._get_subnets_by_network(admin_context,
|
context = qcontext.get_admin_context()
|
||||||
net_id)
|
# start a sub-transaction to avoid breaking parent transactions
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
subnets = self._get_subnets_by_network(context,
|
||||||
|
net_id)
|
||||||
subnets_details = []
|
subnets_details = []
|
||||||
if subnets:
|
if subnets:
|
||||||
for subnet in subnets:
|
for subnet in subnets:
|
||||||
@ -1257,10 +1177,13 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
|
|
||||||
return subnets_details
|
return subnets_details
|
||||||
|
|
||||||
def _get_mapped_network_with_subnets(self, network):
|
def _get_mapped_network_with_subnets(self, network, context=None):
|
||||||
admin_context = qcontext.get_admin_context()
|
# if context is not provided, admin context is used
|
||||||
|
if context is None:
|
||||||
|
context = qcontext.get_admin_context()
|
||||||
network = self._map_state_and_status(network)
|
network = self._map_state_and_status(network)
|
||||||
subnets = self._get_all_subnets_json_for_network(network['id'])
|
subnets = self._get_all_subnets_json_for_network(network['id'],
|
||||||
|
context)
|
||||||
network['subnets'] = subnets
|
network['subnets'] = subnets
|
||||||
for subnet in (subnets or []):
|
for subnet in (subnets or []):
|
||||||
if subnet['gateway_ip']:
|
if subnet['gateway_ip']:
|
||||||
@ -1269,30 +1192,20 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
network['gateway'] = ''
|
network['gateway'] = ''
|
||||||
|
network[l3.EXTERNAL] = self._network_is_external(context,
|
||||||
network[l3.EXTERNAL] = self._network_is_external(admin_context,
|
|
||||||
network['id'])
|
network['id'])
|
||||||
|
|
||||||
return network
|
return network
|
||||||
|
|
||||||
def _send_update_network(self, network):
|
def _send_update_network(self, network, context=None):
|
||||||
net_id = network['id']
|
net_id = network['id']
|
||||||
tenant_id = network['tenant_id']
|
tenant_id = network['tenant_id']
|
||||||
# update network on network controller
|
# update network on network controller
|
||||||
try:
|
mapped_network = self._get_mapped_network_with_subnets(network,
|
||||||
resource = NETWORKS_PATH % (tenant_id, net_id)
|
context)
|
||||||
mapped_network = self._get_mapped_network_with_subnets(network)
|
net_fl_ips = self._get_network_with_floatingips(mapped_network,
|
||||||
net_fl_ips = self._get_network_with_floatingips(mapped_network)
|
context)
|
||||||
data = {
|
self.servers.rest_update_network(tenant_id, net_id, net_fl_ips)
|
||||||
"network": net_fl_ips,
|
|
||||||
}
|
|
||||||
ret = self.servers.put(resource, data)
|
|
||||||
if not self.servers.action_success(ret):
|
|
||||||
raise RemoteRestError(ret[2])
|
|
||||||
except RemoteRestError as e:
|
|
||||||
LOG.error(_("NeutronRestProxyV2: Unable to update remote "
|
|
||||||
"network: %s"), e.message)
|
|
||||||
raise
|
|
||||||
|
|
||||||
def _map_state_and_status(self, resource):
|
def _map_state_and_status(self, resource):
|
||||||
resource = copy.copy(resource)
|
resource = copy.copy(resource)
|
||||||
|
96
neutron/tests/unit/bigswitch/fake_server.py
Normal file
96
neutron/tests/unit/bigswitch/fake_server.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 Big Switch Networks, Inc. All rights reserved.
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
|
# not use this file except in compliance with the License. You may obtain
|
||||||
|
# a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
# License for the specific language governing permissions and limitations
|
||||||
|
# under the License.
|
||||||
|
#
|
||||||
|
# @author: Kevin Benton, <kevin.benton@bigswitch.com>
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPResponseMock():
|
||||||
|
status = 200
|
||||||
|
reason = 'OK'
|
||||||
|
|
||||||
|
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
||||||
|
buffering=False):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return "{'status': '200 OK'}"
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPResponseMock404(HTTPResponseMock):
|
||||||
|
status = 404
|
||||||
|
reason = 'Not Found'
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return "{'status': '404 Not Found'}"
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPResponseMock500(HTTPResponseMock):
|
||||||
|
status = 500
|
||||||
|
reason = 'Internal Server Error'
|
||||||
|
|
||||||
|
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
||||||
|
buffering=False, errmsg='500 Internal Server Error'):
|
||||||
|
self.errmsg = errmsg
|
||||||
|
|
||||||
|
def read(self):
|
||||||
|
return "{'status': '%s'}" % self.errmsg
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPConnectionMock():
|
||||||
|
|
||||||
|
def __init__(self, server, port, timeout):
|
||||||
|
self.response = None
|
||||||
|
self.broken = False
|
||||||
|
# Port 9000 is the broken server
|
||||||
|
if port == 9000:
|
||||||
|
self.broken = True
|
||||||
|
errmsg = "This server is broken, please try another"
|
||||||
|
self.response = HTTPResponseMock500(None, errmsg=errmsg)
|
||||||
|
|
||||||
|
def request(self, action, uri, body, headers):
|
||||||
|
if self.broken and "ExceptOnBadServer" in uri:
|
||||||
|
raise Exception("Broken server got an unexpected request")
|
||||||
|
if self.response:
|
||||||
|
return
|
||||||
|
|
||||||
|
# detachment may return 404 and plugin shouldn't die
|
||||||
|
if uri.endswith('attachment') and action == 'DELETE':
|
||||||
|
self.response = HTTPResponseMock404(None)
|
||||||
|
else:
|
||||||
|
self.response = HTTPResponseMock(None)
|
||||||
|
|
||||||
|
# Port creations/updates must contain binding information
|
||||||
|
if ('port' in uri and 'attachment' not in uri
|
||||||
|
and 'binding' not in body and action in ('POST', 'PUT')):
|
||||||
|
errmsg = "Port binding info missing in port request '%s'" % body
|
||||||
|
self.response = HTTPResponseMock500(None, errmsg=errmsg)
|
||||||
|
return
|
||||||
|
|
||||||
|
return
|
||||||
|
|
||||||
|
def getresponse(self):
|
||||||
|
return self.response
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class HTTPConnectionMock500(HTTPConnectionMock):
|
||||||
|
|
||||||
|
def __init__(self, server, port, timeout):
|
||||||
|
self.response = HTTPResponseMock500(None)
|
||||||
|
self.broken = True
|
@ -22,79 +22,18 @@ from oslo.config import cfg
|
|||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
import neutron.common.test_lib as test_lib
|
import neutron.common.test_lib as test_lib
|
||||||
|
from neutron import context
|
||||||
from neutron.extensions import portbindings
|
from neutron.extensions import portbindings
|
||||||
from neutron.manager import NeutronManager
|
from neutron.manager import NeutronManager
|
||||||
|
from neutron.plugins.bigswitch.plugin import RemoteRestError
|
||||||
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
from neutron.tests.unit import _test_extension_portbindings as test_bindings
|
||||||
|
from neutron.tests.unit.bigswitch import fake_server
|
||||||
|
from neutron.tests.unit import test_api_v2
|
||||||
import neutron.tests.unit.test_db_plugin as test_plugin
|
import neutron.tests.unit.test_db_plugin as test_plugin
|
||||||
|
|
||||||
|
|
||||||
RESTPROXY_PKG_PATH = 'neutron.plugins.bigswitch.plugin'
|
RESTPROXY_PKG_PATH = 'neutron.plugins.bigswitch.plugin'
|
||||||
|
|
||||||
|
|
||||||
class HTTPResponseMock():
|
|
||||||
status = 200
|
|
||||||
reason = 'OK'
|
|
||||||
|
|
||||||
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
|
||||||
buffering=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
return "{'status': '200 OK'}"
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPResponseMock404():
|
|
||||||
status = 404
|
|
||||||
reason = 'Not Found'
|
|
||||||
|
|
||||||
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
|
||||||
buffering=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
return "{'status': '404 Not Found'}"
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPResponseMock500():
|
|
||||||
status = 500
|
|
||||||
reason = 'Internal Server Error'
|
|
||||||
|
|
||||||
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
|
||||||
buffering=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
return "{'status': '500 Internal Server Error'}"
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPConnectionMock():
|
|
||||||
|
|
||||||
def __init__(self, server, port, timeout):
|
|
||||||
if port == 9000:
|
|
||||||
self.response = HTTPResponseMock500(None)
|
|
||||||
self.broken = True
|
|
||||||
else:
|
|
||||||
self.response = HTTPResponseMock(None)
|
|
||||||
self.broken = False
|
|
||||||
|
|
||||||
def request(self, action, uri, body, headers):
|
|
||||||
if self.broken:
|
|
||||||
if "ExceptOnBadServer" in uri:
|
|
||||||
raise Exception("Broken server got an unexpected request")
|
|
||||||
return
|
|
||||||
if uri.endswith('attachment') and action == 'DELETE':
|
|
||||||
self.response = HTTPResponseMock404(None)
|
|
||||||
else:
|
|
||||||
self.response = HTTPResponseMock(None)
|
|
||||||
return
|
|
||||||
|
|
||||||
def getresponse(self):
|
|
||||||
return self.response
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class BigSwitchProxyPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
class BigSwitchProxyPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||||
|
|
||||||
_plugin_name = ('%s.NeutronRestProxyV2' % RESTPROXY_PKG_PATH)
|
_plugin_name = ('%s.NeutronRestProxyV2' % RESTPROXY_PKG_PATH)
|
||||||
@ -105,7 +44,7 @@ class BigSwitchProxyPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||||||
'restproxy.ini.test')]
|
'restproxy.ini.test')]
|
||||||
|
|
||||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
new=HTTPConnectionMock)
|
new=fake_server.HTTPConnectionMock)
|
||||||
self.addCleanup(self.httpPatch.stop)
|
self.addCleanup(self.httpPatch.stop)
|
||||||
self.httpPatch.start()
|
self.httpPatch.start()
|
||||||
super(BigSwitchProxyPluginV2TestCase,
|
super(BigSwitchProxyPluginV2TestCase,
|
||||||
@ -138,6 +77,89 @@ class TestBigSwitchProxyPortsV2(test_plugin.TestPortsV2,
|
|||||||
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
||||||
HAS_PORT_FILTER = False
|
HAS_PORT_FILTER = False
|
||||||
|
|
||||||
|
def _get_ports(self, netid):
|
||||||
|
return self.deserialize('json',
|
||||||
|
self._list_ports('json', netid=netid))['ports']
|
||||||
|
|
||||||
|
def test_rollback_for_port_create(self):
|
||||||
|
with self.network(no_delete=True) as n:
|
||||||
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.httpPatch.start()
|
||||||
|
kwargs = {'device_id': 'somedevid',
|
||||||
|
'tenant_id': n['network']['tenant_id']}
|
||||||
|
self._create_port('json', n['network']['id'],
|
||||||
|
expected_code=
|
||||||
|
webob.exc.HTTPInternalServerError.code,
|
||||||
|
**kwargs)
|
||||||
|
self.httpPatch.stop()
|
||||||
|
ports = self._get_ports(n['network']['id'])
|
||||||
|
#failure to create should result in no ports
|
||||||
|
self.assertEqual(0, len(ports))
|
||||||
|
|
||||||
|
def test_rollback_on_port_attach(self):
|
||||||
|
with self.network() as n:
|
||||||
|
plugin_obj = NeutronManager.get_plugin()
|
||||||
|
with patch.object(plugin_obj.servers,
|
||||||
|
'rest_plug_interface') as mock_plug_interface:
|
||||||
|
mock_plug_interface.side_effect = RemoteRestError('fake error')
|
||||||
|
kwargs = {'device_id': 'somedevid',
|
||||||
|
'tenant_id': n['network']['tenant_id']}
|
||||||
|
self._create_port('json', n['network']['id'],
|
||||||
|
expected_code=
|
||||||
|
webob.exc.HTTPInternalServerError.code,
|
||||||
|
**kwargs)
|
||||||
|
port = self._get_ports(n['network']['id'])[0]
|
||||||
|
# Attachment failure should leave created port in error state
|
||||||
|
self.assertEqual('ERROR', port['status'])
|
||||||
|
self._delete('ports', port['id'])
|
||||||
|
|
||||||
|
def test_rollback_for_port_update(self):
|
||||||
|
with self.network() as n:
|
||||||
|
with self.port(network_id=n['network']['id']) as port:
|
||||||
|
port = self._get_ports(n['network']['id'])[0]
|
||||||
|
data = {'port': {'name': 'aNewName'}}
|
||||||
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.httpPatch.start()
|
||||||
|
self.new_update_request('ports',
|
||||||
|
data,
|
||||||
|
port['id']).get_response(self.api)
|
||||||
|
self.httpPatch.stop()
|
||||||
|
uport = self._get_ports(n['network']['id'])[0]
|
||||||
|
# name should have stayed the same
|
||||||
|
self.assertEqual(port['name'], uport['name'])
|
||||||
|
|
||||||
|
def test_rollback_for_port_detach(self):
|
||||||
|
with self.network() as n:
|
||||||
|
with self.port(network_id=n['network']['id'],
|
||||||
|
device_id='somedevid') as port:
|
||||||
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.httpPatch.start()
|
||||||
|
self._delete('ports', port['port']['id'],
|
||||||
|
expected_code=
|
||||||
|
webob.exc.HTTPInternalServerError.code)
|
||||||
|
self.httpPatch.stop()
|
||||||
|
port = self._get_ports(n['network']['id'])[0]
|
||||||
|
self.assertEqual('ACTIVE', port['status'])
|
||||||
|
|
||||||
|
def test_rollback_for_port_delete(self):
|
||||||
|
with self.network() as n:
|
||||||
|
with self.port(network_id=n['network']['id'],
|
||||||
|
device_id='somdevid') as port:
|
||||||
|
plugin_obj = NeutronManager.get_plugin()
|
||||||
|
with patch.object(plugin_obj.servers,
|
||||||
|
'rest_delete_port'
|
||||||
|
) as mock_plug_interface:
|
||||||
|
mock_plug_interface.side_effect = RemoteRestError(
|
||||||
|
'fake error')
|
||||||
|
self._delete('ports', port['port']['id'],
|
||||||
|
expected_code=
|
||||||
|
webob.exc.HTTPInternalServerError.code)
|
||||||
|
port = self._get_ports(n['network']['id'])[0]
|
||||||
|
self.assertEqual('ERROR', port['status'])
|
||||||
|
|
||||||
|
|
||||||
class TestBigSwitchProxyPortsV2IVS(test_plugin.TestPortsV2,
|
class TestBigSwitchProxyPortsV2IVS(test_plugin.TestPortsV2,
|
||||||
BigSwitchProxyPluginV2TestCase,
|
BigSwitchProxyPluginV2TestCase,
|
||||||
@ -213,7 +235,45 @@ class TestBigSwitchVIFOverride(test_plugin.TestPortsV2,
|
|||||||
class TestBigSwitchProxyNetworksV2(test_plugin.TestNetworksV2,
|
class TestBigSwitchProxyNetworksV2(test_plugin.TestNetworksV2,
|
||||||
BigSwitchProxyPluginV2TestCase):
|
BigSwitchProxyPluginV2TestCase):
|
||||||
|
|
||||||
pass
|
def _get_networks(self, tenant_id):
|
||||||
|
ctx = context.Context('', tenant_id)
|
||||||
|
return NeutronManager.get_plugin().get_networks(ctx)
|
||||||
|
|
||||||
|
def test_rollback_on_network_create(self):
|
||||||
|
tid = test_api_v2._uuid()
|
||||||
|
kwargs = {'tenant_id': tid}
|
||||||
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.httpPatch.start()
|
||||||
|
self._create_network('json', 'netname', True, **kwargs)
|
||||||
|
self.httpPatch.stop()
|
||||||
|
self.assertFalse(self._get_networks(tid))
|
||||||
|
|
||||||
|
def test_rollback_on_network_update(self):
|
||||||
|
with self.network() as n:
|
||||||
|
data = {'network': {'name': 'aNewName'}}
|
||||||
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.httpPatch.start()
|
||||||
|
self.new_update_request('networks', data,
|
||||||
|
n['network']['id']).get_response(self.api)
|
||||||
|
self.httpPatch.stop()
|
||||||
|
updatedn = self._get_networks(n['network']['tenant_id'])[0]
|
||||||
|
# name should have stayed the same due to failure
|
||||||
|
self.assertEqual(n['network']['name'], updatedn['name'])
|
||||||
|
|
||||||
|
def test_rollback_on_network_delete(self):
|
||||||
|
with self.network() as n:
|
||||||
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.httpPatch.start()
|
||||||
|
self._delete('networks', n['network']['id'],
|
||||||
|
expected_code=webob.exc.HTTPInternalServerError.code)
|
||||||
|
self.httpPatch.stop()
|
||||||
|
# network should still exist in db
|
||||||
|
self.assertEqual(n['network']['id'],
|
||||||
|
self._get_networks(n['network']['tenant_id']
|
||||||
|
)[0]['id'])
|
||||||
|
|
||||||
|
|
||||||
class TestBigSwitchProxySubnetsV2(test_plugin.TestSubnetsV2,
|
class TestBigSwitchProxySubnetsV2(test_plugin.TestSubnetsV2,
|
||||||
|
@ -26,11 +26,14 @@ from oslo.config import cfg
|
|||||||
from webob import exc
|
from webob import exc
|
||||||
|
|
||||||
from neutron.common.test_lib import test_config
|
from neutron.common.test_lib import test_config
|
||||||
|
from neutron import context
|
||||||
from neutron.extensions import l3
|
from neutron.extensions import l3
|
||||||
from neutron.manager import NeutronManager
|
from neutron.manager import NeutronManager
|
||||||
from neutron.openstack.common.notifier import api as notifier_api
|
from neutron.openstack.common.notifier import api as notifier_api
|
||||||
from neutron.openstack.common.notifier import test_notifier
|
from neutron.openstack.common.notifier import test_notifier
|
||||||
from neutron.plugins.bigswitch.extensions import routerrule
|
from neutron.plugins.bigswitch.extensions import routerrule
|
||||||
|
from neutron.tests.unit.bigswitch import fake_server
|
||||||
|
from neutron.tests.unit import test_api_v2
|
||||||
from neutron.tests.unit import test_extension_extradhcpopts as test_extradhcp
|
from neutron.tests.unit import test_extension_extradhcpopts as test_extradhcp
|
||||||
from neutron.tests.unit import test_l3_plugin
|
from neutron.tests.unit import test_l3_plugin
|
||||||
|
|
||||||
@ -54,51 +57,6 @@ def new_L3_setUp(self):
|
|||||||
origSetUp = test_l3_plugin.L3NatDBTestCase.setUp
|
origSetUp = test_l3_plugin.L3NatDBTestCase.setUp
|
||||||
|
|
||||||
|
|
||||||
class HTTPResponseMock():
|
|
||||||
status = 200
|
|
||||||
reason = 'OK'
|
|
||||||
|
|
||||||
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
|
||||||
buffering=False):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
return "{'status': '200 OK'}"
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPResponseMock500():
|
|
||||||
status = 500
|
|
||||||
reason = 'Internal Server Error'
|
|
||||||
|
|
||||||
def __init__(self, sock, debuglevel=0, strict=0, method=None,
|
|
||||||
buffering=False, errmsg='500 Internal Server Error'):
|
|
||||||
self.errmsg = errmsg
|
|
||||||
|
|
||||||
def read(self):
|
|
||||||
return "{'status': '%s'}" % self.errmsg
|
|
||||||
|
|
||||||
|
|
||||||
class HTTPConnectionMock():
|
|
||||||
|
|
||||||
def __init__(self, server, port, timeout):
|
|
||||||
self.response = None
|
|
||||||
|
|
||||||
def request(self, action, uri, body, headers):
|
|
||||||
self.response = HTTPResponseMock(None)
|
|
||||||
# Port creations/updates must contain binding information
|
|
||||||
if ('port' in uri and 'attachment' not in uri
|
|
||||||
and 'binding' not in body and action in ('POST', 'PUT')):
|
|
||||||
errmsg = "Port binding info missing in port request '%s'" % body
|
|
||||||
self.response = HTTPResponseMock500(None, errmsg=errmsg)
|
|
||||||
return
|
|
||||||
|
|
||||||
def getresponse(self):
|
|
||||||
return self.response
|
|
||||||
|
|
||||||
def close(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class RouterRulesTestExtensionManager(object):
|
class RouterRulesTestExtensionManager(object):
|
||||||
|
|
||||||
def get_resources(self):
|
def get_resources(self):
|
||||||
@ -117,7 +75,7 @@ class DHCPOptsTestCase(test_extradhcp.TestExtraDhcpOpt):
|
|||||||
|
|
||||||
def setUp(self, plugin=None):
|
def setUp(self, plugin=None):
|
||||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
new=HTTPConnectionMock)
|
new=fake_server.HTTPConnectionMock)
|
||||||
self.httpPatch.start()
|
self.httpPatch.start()
|
||||||
self.addCleanup(self.httpPatch.stop)
|
self.addCleanup(self.httpPatch.stop)
|
||||||
p_path = 'neutron.plugins.bigswitch.plugin.NeutronRestProxyV2'
|
p_path = 'neutron.plugins.bigswitch.plugin.NeutronRestProxyV2'
|
||||||
@ -128,7 +86,7 @@ class RouterDBTestCase(test_l3_plugin.L3NatDBTestCase):
|
|||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
new=HTTPConnectionMock)
|
new=fake_server.HTTPConnectionMock)
|
||||||
self.httpPatch.start()
|
self.httpPatch.start()
|
||||||
test_l3_plugin.L3NatDBTestCase.setUp = new_L3_setUp
|
test_l3_plugin.L3NatDBTestCase.setUp = new_L3_setUp
|
||||||
super(RouterDBTestCase, self).setUp()
|
super(RouterDBTestCase, self).setUp()
|
||||||
@ -498,6 +456,44 @@ class RouterDBTestCase(test_l3_plugin.L3NatDBTestCase):
|
|||||||
{'router': {'router_rules': rules}},
|
{'router': {'router_rules': rules}},
|
||||||
expected_code=exc.HTTPBadRequest.code)
|
expected_code=exc.HTTPBadRequest.code)
|
||||||
|
|
||||||
|
def test_rollback_on_router_create(self):
|
||||||
|
tid = test_api_v2._uuid()
|
||||||
|
self.errhttpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.errhttpPatch.start()
|
||||||
|
self._create_router('json', tid)
|
||||||
|
self.errhttpPatch.stop()
|
||||||
|
self.assertTrue(len(self._get_routers(tid)) == 0)
|
||||||
|
|
||||||
|
def test_rollback_on_router_update(self):
|
||||||
|
with self.router() as r:
|
||||||
|
data = {'router': {'name': 'aNewName'}}
|
||||||
|
self.errhttpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.errhttpPatch.start()
|
||||||
|
self.new_update_request('routers', data,
|
||||||
|
r['router']['id']).get_response(self.api)
|
||||||
|
self.errhttpPatch.stop()
|
||||||
|
updatedr = self._get_routers(r['router']['tenant_id'])[0]
|
||||||
|
# name should have stayed the same due to failure
|
||||||
|
self.assertEqual(r['router']['name'], updatedr['name'])
|
||||||
|
|
||||||
|
def test_rollback_on_router_delete(self):
|
||||||
|
with self.router() as r:
|
||||||
|
self.errhttpPatch = patch('httplib.HTTPConnection', create=True,
|
||||||
|
new=fake_server.HTTPConnectionMock500)
|
||||||
|
self.errhttpPatch.start()
|
||||||
|
self._delete('routers', r['router']['id'],
|
||||||
|
expected_code=exc.HTTPInternalServerError.code)
|
||||||
|
self.errhttpPatch.stop()
|
||||||
|
self.assertEqual(r['router']['id'],
|
||||||
|
self._get_routers(r['router']['tenant_id']
|
||||||
|
)[0]['id'])
|
||||||
|
|
||||||
|
def _get_routers(self, tenant_id):
|
||||||
|
ctx = context.Context('', tenant_id)
|
||||||
|
return self.plugin_obj.get_routers(ctx)
|
||||||
|
|
||||||
|
|
||||||
def _strip_rule_ids(rules):
|
def _strip_rule_ids(rules):
|
||||||
cleaned = []
|
cleaned = []
|
||||||
|
Loading…
Reference in New Issue
Block a user