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 l3
|
||||
from neutron.extensions import portbindings
|
||||
from neutron.openstack.common import excutils
|
||||
from neutron.openstack.common import log as logging
|
||||
from neutron.openstack.common import rpc
|
||||
from neutron.plugins.bigswitch.db import porttracker_db
|
||||
@ -302,6 +303,10 @@ class ServerPool(object):
|
||||
'server': (active_server.server,
|
||||
active_server.port),
|
||||
'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
|
||||
|
||||
# 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)})
|
||||
return (0, None, None, None)
|
||||
|
||||
def get(self, resource, data='', headers=None, ignore_codes=[]):
|
||||
return self.rest_call('GET', resource, data, headers, ignore_codes)
|
||||
def rest_action(self, action, resource, data='', errstr='%s',
|
||||
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=[]):
|
||||
return self.rest_call('PUT', resource, data, headers, ignore_codes)
|
||||
def rest_create_router(self, tenant_id, router):
|
||||
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=[]):
|
||||
return self.rest_call('POST', resource, data, headers, ignore_codes)
|
||||
def rest_update_router(self, tenant_id, router, router_id):
|
||||
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=[]):
|
||||
return self.rest_call('DELETE', resource, data, headers, ignore_codes)
|
||||
def rest_delete_router(self, tenant_id, router_id):
|
||||
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):
|
||||
@ -413,32 +507,20 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
self._warn_on_state_status(network['network'])
|
||||
|
||||
# Validate args
|
||||
tenant_id = self._get_tenant_id_for_create(context, network["network"])
|
||||
with context.session.begin(subtransactions=True):
|
||||
# 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
|
||||
new_net = super(NeutronRestProxyV2, self).create_network(context,
|
||||
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
|
||||
try:
|
||||
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
|
||||
# create network on the network controller
|
||||
self.servers.rest_create_network(tenant_id, mapped_network)
|
||||
|
||||
# return created network
|
||||
return new_net
|
||||
@ -472,25 +554,12 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
new_net = super(NeutronRestProxyV2, self).update_network(context,
|
||||
net_id,
|
||||
network)
|
||||
new_net = super(NeutronRestProxyV2, self).update_network(
|
||||
context, net_id, network)
|
||||
self._process_l3_update(context, new_net, network['network'])
|
||||
|
||||
# update network on network controller
|
||||
try:
|
||||
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
|
||||
# update network on network controller
|
||||
self._send_update_network(new_net, context)
|
||||
return new_net
|
||||
|
||||
def delete_network(self, context, net_id):
|
||||
@ -520,20 +589,11 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
if not only_auto_del:
|
||||
raise exceptions.NetworkInUse(net_id=net_id)
|
||||
|
||||
# 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])
|
||||
with context.session.begin(subtransactions=True):
|
||||
ret_val = super(NeutronRestProxyV2, self).delete_network(context,
|
||||
net_id)
|
||||
self.servers.rest_delete_network(tenant_id, net_id)
|
||||
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):
|
||||
"""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"))
|
||||
|
||||
# Update DB
|
||||
port["port"]["admin_state_up"] = False
|
||||
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
|
||||
new_port = super(NeutronRestProxyV2, self).create_port(context, 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])
|
||||
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"])
|
||||
# Update DB in new session so exceptions rollback changes
|
||||
with context.session.begin(subtransactions=True):
|
||||
port["port"]["admin_state_up"] = False
|
||||
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
|
||||
new_port = super(NeutronRestProxyV2, self).create_port(context,
|
||||
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)
|
||||
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 new_port['device_owner'] == 'network:dhcp':
|
||||
destination = METADATA_SERVER_IP + '/32'
|
||||
self._add_host_route(context, destination, new_port)
|
||||
if self.add_meta_server_route:
|
||||
if new_port['device_owner'] == 'network:dhcp':
|
||||
destination = METADATA_SERVER_IP + '/32'
|
||||
self._add_host_route(context, destination, new_port)
|
||||
|
||||
# create on networl ctrl
|
||||
try:
|
||||
resource = PORT_RESOURCE_PATH % (net["tenant_id"], net["id"])
|
||||
# create on network ctrl
|
||||
mapped_port = self._map_state_and_status(new_port)
|
||||
data = {
|
||||
"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
|
||||
self.servers.rest_create_port(net, mapped_port)
|
||||
|
||||
# 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
|
||||
port_update = {"port": {"admin_state_up": True}}
|
||||
new_port = super(NeutronRestProxyV2, self).update_port(context,
|
||||
@ -660,44 +718,45 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
# Validate Args
|
||||
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
|
||||
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)
|
||||
# update on networl ctrl
|
||||
mapped_port = self._map_state_and_status(new_port)
|
||||
data = {"port": mapped_port}
|
||||
ret = self.servers.put(resource, data)
|
||||
if not self.servers.action_success(ret):
|
||||
raise RemoteRestError(ret[2])
|
||||
self.servers.rest_update_port(orig_port["tenant_id"],
|
||||
orig_port["network_id"],
|
||||
mapped_port, port_id)
|
||||
|
||||
if new_port.get("device_id") != orig_port.get("device_id"):
|
||||
if orig_port.get("device_id"):
|
||||
self._unplug_interface(context, orig_port["tenant_id"],
|
||||
orig_port["network_id"],
|
||||
orig_port["id"])
|
||||
if (new_port.get("device_id") != orig_port.get("device_id") and
|
||||
orig_port.get("device_id")):
|
||||
try:
|
||||
self.servers.rest_unplug_interface(orig_port["tenant_id"],
|
||||
orig_port["network_id"],
|
||||
orig_port["id"])
|
||||
device_id = new_port.get("device_id")
|
||||
if device_id:
|
||||
self._plug_interface(context, new_port["tenant_id"],
|
||||
new_port["network_id"],
|
||||
new_port["id"], device_id)
|
||||
self.rest_plug_interface(new_port["tenant_id"],
|
||||
new_port["network_id"],
|
||||
new_port, device_id)
|
||||
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("NeutronRestProxyV2: Unable to create remote port: "
|
||||
"%s"), e.message)
|
||||
# reset port to original state
|
||||
super(NeutronRestProxyV2, self).update_port(context, port_id,
|
||||
orig_port)
|
||||
raise
|
||||
except RemoteRestError:
|
||||
with excutils.save_and_reraise_exception():
|
||||
port_update = {"port": {"status": "ERROR"}}
|
||||
super(NeutronRestProxyV2, self).update_port(
|
||||
context,
|
||||
new_port["id"],
|
||||
port_update
|
||||
)
|
||||
|
||||
# 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.
|
||||
if l3_port_check:
|
||||
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 _delete_port(self, context, port_id):
|
||||
# Delete from DB
|
||||
def _unplug_port(self, context, port_id):
|
||||
port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
||||
tenant_id = port['tenant_id']
|
||||
net_id = port['network_id']
|
||||
if tenant_id == '':
|
||||
net = super(NeutronRestProxyV2,
|
||||
self).get_network(context, port['network_id'])
|
||||
net = super(NeutronRestProxyV2, self).get_network(context, net_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
|
||||
try:
|
||||
resource = PORTS_PATH % (tenant_id, port["network_id"],
|
||||
port_id)
|
||||
ret = self.servers.delete(resource)
|
||||
if not self.servers.action_success(ret):
|
||||
raise RemoteRestError(ret[2])
|
||||
|
||||
if port.get("device_id"):
|
||||
self._unplug_interface(context, tenant_id,
|
||||
port["network_id"], port["id"])
|
||||
ret_val = super(NeutronRestProxyV2, self)._delete_port(context,
|
||||
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 _delete_port(self, context, port_id):
|
||||
port = super(NeutronRestProxyV2, self).get_port(context, port_id)
|
||||
tenant_id = port['tenant_id']
|
||||
net_id = port['network_id']
|
||||
if tenant_id == '':
|
||||
net = super(NeutronRestProxyV2, self).get_network(context, net_id)
|
||||
tenant_id = net['tenant_id']
|
||||
# Delete from DB
|
||||
ret_val = super(NeutronRestProxyV2,
|
||||
self)._delete_port(context, port_id)
|
||||
self.servers.rest_delete_port(tenant_id, net_id, port_id)
|
||||
return ret_val
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
LOG.debug(_("NeutronRestProxyV2: create_subnet() called"))
|
||||
|
||||
self._warn_on_state_status(subnet['subnet'])
|
||||
|
||||
# create subnet in DB
|
||||
new_subnet = super(NeutronRestProxyV2, self).create_subnet(context,
|
||||
subnet)
|
||||
net_id = new_subnet['network_id']
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
with context.session.begin(subtransactions=True):
|
||||
# create subnet in DB
|
||||
new_subnet = super(NeutronRestProxyV2,
|
||||
self).create_subnet(context, subnet)
|
||||
net_id = new_subnet['network_id']
|
||||
orig_net = super(NeutronRestProxyV2,
|
||||
self).get_network(context, net_id)
|
||||
# update network on network controller
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError:
|
||||
# rollback creation of subnet
|
||||
super(NeutronRestProxyV2, self).delete_subnet(context,
|
||||
subnet['id'])
|
||||
raise
|
||||
return new_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'])
|
||||
|
||||
orig_subnet = super(NeutronRestProxyV2, self)._get_subnet(context, id)
|
||||
|
||||
# update subnet in DB
|
||||
new_subnet = super(NeutronRestProxyV2, self).update_subnet(context, id,
|
||||
subnet)
|
||||
net_id = new_subnet['network_id']
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
with context.session.begin(subtransactions=True):
|
||||
# update subnet in DB
|
||||
new_subnet = super(NeutronRestProxyV2,
|
||||
self).update_subnet(context, id, subnet)
|
||||
net_id = new_subnet['network_id']
|
||||
orig_net = super(NeutronRestProxyV2,
|
||||
self).get_network(context, net_id)
|
||||
# update network on network controller
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError:
|
||||
# rollback updation of subnet
|
||||
super(NeutronRestProxyV2, self).update_subnet(context, id,
|
||||
orig_subnet)
|
||||
raise
|
||||
return new_subnet
|
||||
return new_subnet
|
||||
|
||||
def delete_subnet(self, context, id):
|
||||
LOG.debug(_("NeutronRestProxyV2: delete_subnet() called"))
|
||||
orig_subnet = super(NeutronRestProxyV2, self).get_subnet(context, id)
|
||||
net_id = orig_subnet['network_id']
|
||||
# delete subnet in DB
|
||||
super(NeutronRestProxyV2, self).delete_subnet(context, id)
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
with context.session.begin(subtransactions=True):
|
||||
# delete subnet in DB
|
||||
super(NeutronRestProxyV2, self).delete_subnet(context, id)
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller - exception will rollback
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError:
|
||||
# TODO(Sumit): rollback deletion of subnet
|
||||
raise
|
||||
|
||||
def _get_tenant_default_router_rules(self, tenant):
|
||||
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)
|
||||
router['router']['router_rules'] = rules
|
||||
|
||||
# create router in DB
|
||||
new_router = super(NeutronRestProxyV2, self).create_router(context,
|
||||
router)
|
||||
|
||||
# create router on the network controller
|
||||
try:
|
||||
resource = ROUTER_RESOURCE_PATH % tenant_id
|
||||
with context.session.begin(subtransactions=True):
|
||||
# create router in DB
|
||||
new_router = super(NeutronRestProxyV2, self).create_router(context,
|
||||
router)
|
||||
mapped_router = self._map_state_and_status(new_router)
|
||||
data = {
|
||||
"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
|
||||
self.servers.rest_create_router(tenant_id, mapped_router)
|
||||
|
||||
# return created router
|
||||
return new_router
|
||||
# return created router
|
||||
return new_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,
|
||||
router_id)
|
||||
tenant_id = orig_router["tenant_id"]
|
||||
new_router = super(NeutronRestProxyV2, self).update_router(context,
|
||||
router_id,
|
||||
router)
|
||||
with context.session.begin(subtransactions=True):
|
||||
new_router = super(NeutronRestProxyV2,
|
||||
self).update_router(context, router_id, router)
|
||||
router = self._map_state_and_status(new_router)
|
||||
|
||||
# update router on network controller
|
||||
try:
|
||||
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
|
||||
# update router on network controller
|
||||
self.servers.rest_update_router(tenant_id, router, router_id)
|
||||
|
||||
# return updated router
|
||||
return new_router
|
||||
# return updated router
|
||||
return new_router
|
||||
|
||||
def delete_router(self, context, router_id):
|
||||
LOG.debug(_("NeutronRestProxyV2: delete_router() called"))
|
||||
@ -985,20 +947,12 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
filters=device_filter)
|
||||
if ports:
|
||||
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
|
||||
try:
|
||||
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)
|
||||
# delete from network ctrl
|
||||
self.servers.rest_delete_router(tenant_id, router_id)
|
||||
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):
|
||||
|
||||
@ -1008,36 +962,25 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
router = self._get_router(context, router_id)
|
||||
tenant_id = router['tenant_id']
|
||||
|
||||
# create interface in DB
|
||||
new_interface_info = super(NeutronRestProxyV2,
|
||||
self).add_router_interface(context,
|
||||
router_id,
|
||||
interface_info)
|
||||
port = self._get_port(context, new_interface_info['port_id'])
|
||||
net_id = port['network_id']
|
||||
subnet_id = new_interface_info['subnet_id']
|
||||
# we will use the port's network id as interface's id
|
||||
interface_id = net_id
|
||||
intf_details = self._get_router_intf_details(context,
|
||||
interface_id,
|
||||
subnet_id)
|
||||
with context.session.begin(subtransactions=True):
|
||||
# create interface in DB
|
||||
new_intf_info = super(NeutronRestProxyV2,
|
||||
self).add_router_interface(context,
|
||||
router_id,
|
||||
interface_info)
|
||||
port = self._get_port(context, new_intf_info['port_id'])
|
||||
net_id = port['network_id']
|
||||
subnet_id = new_intf_info['subnet_id']
|
||||
# we will use the port's network id as interface's id
|
||||
interface_id = net_id
|
||||
intf_details = self._get_router_intf_details(context,
|
||||
interface_id,
|
||||
subnet_id)
|
||||
|
||||
# create interface on the network controller
|
||||
try:
|
||||
resource = ROUTER_INTF_OP_PATH % (tenant_id, router_id)
|
||||
data = {"interface": intf_details}
|
||||
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
|
||||
# create interface on the network controller
|
||||
self.servers.rest_add_router_interface(tenant_id, router_id,
|
||||
intf_details)
|
||||
return new_intf_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"
|
||||
raise exceptions.BadRequest(resource='router', msg=msg)
|
||||
|
||||
# remove router in DB
|
||||
del_intf_info = super(NeutronRestProxyV2,
|
||||
self).remove_router_interface(context,
|
||||
router_id,
|
||||
interface_info)
|
||||
with context.session.begin(subtransactions=True):
|
||||
# remove router in DB
|
||||
del_ret = super(NeutronRestProxyV2,
|
||||
self).remove_router_interface(context,
|
||||
router_id,
|
||||
interface_info)
|
||||
|
||||
# create router on the network controller
|
||||
try:
|
||||
resource = ROUTER_INTF_PATH % (tenant_id, router_id, interface_id)
|
||||
ret = self.servers.delete(resource)
|
||||
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
|
||||
# create router on the network controller
|
||||
self.servers.rest_remove_router_interface(tenant_id, router_id,
|
||||
interface_id)
|
||||
return del_ret
|
||||
|
||||
def create_floatingip(self, context, floatingip):
|
||||
LOG.debug(_("NeutronRestProxyV2: create_floatingip() called"))
|
||||
|
||||
# create floatingip in DB
|
||||
new_fl_ip = super(NeutronRestProxyV2,
|
||||
self).create_floatingip(context, floatingip)
|
||||
with context.session.begin(subtransactions=True):
|
||||
# create floatingip in DB
|
||||
new_fl_ip = super(NeutronRestProxyV2,
|
||||
self).create_floatingip(context, floatingip)
|
||||
|
||||
net_id = new_fl_ip['floating_network_id']
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# create floatingip on the network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("NeutronRestProxyV2: Unable to create remote "
|
||||
"floatin IP: %s"), e.message)
|
||||
super(NeutronRestProxyV2, self).delete_floatingip(context,
|
||||
floatingip)
|
||||
raise
|
||||
|
||||
# return created floating IP
|
||||
return new_fl_ip
|
||||
net_id = new_fl_ip['floating_network_id']
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# create floatingip on the network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
LOG.error(
|
||||
_("NeutronRestProxyV2: Unable to create remote "
|
||||
"floating IP: %s"), e)
|
||||
# return created floating IP
|
||||
return new_fl_ip
|
||||
|
||||
def update_floatingip(self, context, id, floatingip):
|
||||
LOG.debug(_("NeutronRestProxyV2: update_floatingip() called"))
|
||||
|
||||
orig_fl_ip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
||||
id)
|
||||
with context.session.begin(subtransactions=True):
|
||||
# update floatingip in DB
|
||||
new_fl_ip = super(NeutronRestProxyV2,
|
||||
self).update_floatingip(context, id, floatingip)
|
||||
|
||||
# update floatingip in DB
|
||||
new_fl_ip = super(NeutronRestProxyV2,
|
||||
self).update_floatingip(context, id, floatingip)
|
||||
|
||||
net_id = new_fl_ip['floating_network_id']
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
net_id = new_fl_ip['floating_network_id']
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError:
|
||||
# rollback updation of subnet
|
||||
super(NeutronRestProxyV2, self).update_floatingip(context, id,
|
||||
orig_fl_ip)
|
||||
raise
|
||||
return new_fl_ip
|
||||
return new_fl_ip
|
||||
|
||||
def delete_floatingip(self, context, id):
|
||||
LOG.debug(_("NeutronRestProxyV2: delete_floatingip() called"))
|
||||
|
||||
orig_fl_ip = super(NeutronRestProxyV2, self).get_floatingip(context,
|
||||
id)
|
||||
# delete floating IP in DB
|
||||
net_id = orig_fl_ip['floating_network_id']
|
||||
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
||||
with context.session.begin(subtransactions=True):
|
||||
# delete floating IP in DB
|
||||
net_id = orig_fl_ip['floating_network_id']
|
||||
super(NeutronRestProxyV2, self).delete_floatingip(context, id)
|
||||
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
orig_net = super(NeutronRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError:
|
||||
# TODO(Sumit): rollback deletion of floating IP
|
||||
raise
|
||||
|
||||
def _send_all_data(self):
|
||||
"""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)
|
||||
|
||||
try:
|
||||
resource = '/topology'
|
||||
data = {
|
||||
'networks': networks,
|
||||
'routers': routers,
|
||||
}
|
||||
ret = self.servers.put(resource, data)
|
||||
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
|
||||
resource = '/topology'
|
||||
data = {
|
||||
'networks': networks,
|
||||
'routers': routers,
|
||||
}
|
||||
errstr = _("Unable to update remote topology: %s")
|
||||
return self.servers.rest_action('PUT', resource, data, errstr)
|
||||
|
||||
def _add_host_route(self, context, destination, port):
|
||||
subnet = {}
|
||||
@ -1232,22 +1148,26 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
LOG.debug("destination:%s nexthop:%s" % (destination,
|
||||
nexthop))
|
||||
|
||||
def _get_network_with_floatingips(self, network):
|
||||
admin_context = qcontext.get_admin_context()
|
||||
def _get_network_with_floatingips(self, network, context=None):
|
||||
if context is None:
|
||||
context = qcontext.get_admin_context()
|
||||
|
||||
net_id = network['id']
|
||||
net_filter = {'floating_network_id': [net_id]}
|
||||
fl_ips = super(NeutronRestProxyV2,
|
||||
self).get_floatingips(admin_context,
|
||||
self).get_floatingips(context,
|
||||
filters=net_filter) or []
|
||||
network['floatingips'] = fl_ips
|
||||
|
||||
return network
|
||||
|
||||
def _get_all_subnets_json_for_network(self, net_id):
|
||||
admin_context = qcontext.get_admin_context()
|
||||
subnets = self._get_subnets_by_network(admin_context,
|
||||
net_id)
|
||||
def _get_all_subnets_json_for_network(self, net_id, context=None):
|
||||
if context is None:
|
||||
context = qcontext.get_admin_context()
|
||||
# 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 = []
|
||||
if subnets:
|
||||
for subnet in subnets:
|
||||
@ -1257,10 +1177,13 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
|
||||
return subnets_details
|
||||
|
||||
def _get_mapped_network_with_subnets(self, network):
|
||||
admin_context = qcontext.get_admin_context()
|
||||
def _get_mapped_network_with_subnets(self, network, context=None):
|
||||
# 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)
|
||||
subnets = self._get_all_subnets_json_for_network(network['id'])
|
||||
subnets = self._get_all_subnets_json_for_network(network['id'],
|
||||
context)
|
||||
network['subnets'] = subnets
|
||||
for subnet in (subnets or []):
|
||||
if subnet['gateway_ip']:
|
||||
@ -1269,30 +1192,20 @@ class NeutronRestProxyV2(db_base_plugin_v2.NeutronDbPluginV2,
|
||||
break
|
||||
else:
|
||||
network['gateway'] = ''
|
||||
|
||||
network[l3.EXTERNAL] = self._network_is_external(admin_context,
|
||||
network[l3.EXTERNAL] = self._network_is_external(context,
|
||||
network['id'])
|
||||
|
||||
return network
|
||||
|
||||
def _send_update_network(self, network):
|
||||
def _send_update_network(self, network, context=None):
|
||||
net_id = network['id']
|
||||
tenant_id = network['tenant_id']
|
||||
# update network on network controller
|
||||
try:
|
||||
resource = NETWORKS_PATH % (tenant_id, net_id)
|
||||
mapped_network = self._get_mapped_network_with_subnets(network)
|
||||
net_fl_ips = self._get_network_with_floatingips(mapped_network)
|
||||
data = {
|
||||
"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
|
||||
mapped_network = self._get_mapped_network_with_subnets(network,
|
||||
context)
|
||||
net_fl_ips = self._get_network_with_floatingips(mapped_network,
|
||||
context)
|
||||
self.servers.rest_update_network(tenant_id, net_id, net_fl_ips)
|
||||
|
||||
def _map_state_and_status(self, 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 neutron.common.test_lib as test_lib
|
||||
from neutron import context
|
||||
from neutron.extensions import portbindings
|
||||
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.bigswitch import fake_server
|
||||
from neutron.tests.unit import test_api_v2
|
||||
import neutron.tests.unit.test_db_plugin as test_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):
|
||||
|
||||
_plugin_name = ('%s.NeutronRestProxyV2' % RESTPROXY_PKG_PATH)
|
||||
@ -105,7 +44,7 @@ class BigSwitchProxyPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||
'restproxy.ini.test')]
|
||||
|
||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||
new=HTTPConnectionMock)
|
||||
new=fake_server.HTTPConnectionMock)
|
||||
self.addCleanup(self.httpPatch.stop)
|
||||
self.httpPatch.start()
|
||||
super(BigSwitchProxyPluginV2TestCase,
|
||||
@ -138,6 +77,89 @@ class TestBigSwitchProxyPortsV2(test_plugin.TestPortsV2,
|
||||
VIF_TYPE = portbindings.VIF_TYPE_OVS
|
||||
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,
|
||||
BigSwitchProxyPluginV2TestCase,
|
||||
@ -213,7 +235,45 @@ class TestBigSwitchVIFOverride(test_plugin.TestPortsV2,
|
||||
class TestBigSwitchProxyNetworksV2(test_plugin.TestNetworksV2,
|
||||
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,
|
||||
|
@ -26,11 +26,14 @@ from oslo.config import cfg
|
||||
from webob import exc
|
||||
|
||||
from neutron.common.test_lib import test_config
|
||||
from neutron import context
|
||||
from neutron.extensions import l3
|
||||
from neutron.manager import NeutronManager
|
||||
from neutron.openstack.common.notifier import api as notifier_api
|
||||
from neutron.openstack.common.notifier import test_notifier
|
||||
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_l3_plugin
|
||||
|
||||
@ -54,51 +57,6 @@ def new_L3_setUp(self):
|
||||
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):
|
||||
|
||||
def get_resources(self):
|
||||
@ -117,7 +75,7 @@ class DHCPOptsTestCase(test_extradhcp.TestExtraDhcpOpt):
|
||||
|
||||
def setUp(self, plugin=None):
|
||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||
new=HTTPConnectionMock)
|
||||
new=fake_server.HTTPConnectionMock)
|
||||
self.httpPatch.start()
|
||||
self.addCleanup(self.httpPatch.stop)
|
||||
p_path = 'neutron.plugins.bigswitch.plugin.NeutronRestProxyV2'
|
||||
@ -128,7 +86,7 @@ class RouterDBTestCase(test_l3_plugin.L3NatDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||
new=HTTPConnectionMock)
|
||||
new=fake_server.HTTPConnectionMock)
|
||||
self.httpPatch.start()
|
||||
test_l3_plugin.L3NatDBTestCase.setUp = new_L3_setUp
|
||||
super(RouterDBTestCase, self).setUp()
|
||||
@ -498,6 +456,44 @@ class RouterDBTestCase(test_l3_plugin.L3NatDBTestCase):
|
||||
{'router': {'router_rules': rules}},
|
||||
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):
|
||||
cleaned = []
|
||||
|
Loading…
Reference in New Issue
Block a user