L3 API support for BigSwitch-FloodLight Plugin
In keeping with the philosophy of the RESTProxy plugin, L3 extension calls are processed (CRUD of logical resources) and the state changes are proxied to a backend controller. A configuration variable specific to the RESTProxy plugin is being added to identify that particular Quantum server's ID. blueprint quantum-floodlight-bigswitch-l3 Change-Id: I24be02cd836352497fe5b1e7622d138e0c816377
This commit is contained in:
parent
a7837e5f90
commit
6020c897ab
@ -28,13 +28,13 @@ reconnect_interval = 2
|
||||
#
|
||||
# The following parameters are supported:
|
||||
# servers : <host:port>[,<host:port>]* (Error if not set)
|
||||
# serverauth : <username:password> (default: no auth)
|
||||
# serverssl : True | False (default: False)
|
||||
# syncdata : True | False (default: False)
|
||||
# servertimeout : 10 (default: 10 seconds)
|
||||
# server_auth : <username:password> (default: no auth)
|
||||
# server_ssl : True | False (default: False)
|
||||
# sync_data : True | False (default: False)
|
||||
# server_timeout : 10 (default: 10 seconds)
|
||||
#
|
||||
servers=localhost:8080
|
||||
#serverauth=username:password
|
||||
#serverssl=True
|
||||
#syncdata=True
|
||||
#servertimeout=10
|
||||
#server_auth=username:password
|
||||
#server_ssl=True
|
||||
#sync_data=True
|
||||
#server_timeout=10
|
||||
|
@ -45,18 +45,24 @@ on port-attach) on an additional PUT to do a bulk dump of all persistent data.
|
||||
"""
|
||||
|
||||
import base64
|
||||
import copy
|
||||
import httplib
|
||||
import json
|
||||
import socket
|
||||
|
||||
from quantum.common import constants as const
|
||||
from quantum.common import exceptions
|
||||
from quantum.common import rpc as q_rpc
|
||||
from quantum.common import topics
|
||||
from quantum.common import utils
|
||||
from quantum import context as qcontext
|
||||
from quantum.db import api as db
|
||||
from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import dhcp_rpc_base
|
||||
from quantum.db import l3_db
|
||||
from quantum.extensions import l3
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.openstack.common import lockutils
|
||||
from quantum.openstack.common import log as logging
|
||||
from quantum.openstack.common import rpc
|
||||
from quantum.plugins.bigswitch.version import version_string_with_vcs
|
||||
@ -69,16 +75,20 @@ restproxy_opts = [
|
||||
cfg.StrOpt('servers', default='localhost:8800',
|
||||
help=_("A comma separated list of servers and port numbers "
|
||||
"to proxy request to.")),
|
||||
cfg.StrOpt('serverauth', default='username:password',
|
||||
help=_("Server authentication"),
|
||||
secret=True),
|
||||
cfg.BoolOpt('serverssl', default=False,
|
||||
cfg.StrOpt('server_auth', default='username:password', secret=True,
|
||||
help=_("Server authentication")),
|
||||
cfg.BoolOpt('server_ssl', default=False,
|
||||
help=_("Use SSL to connect")),
|
||||
cfg.BoolOpt('syncdata', default=False,
|
||||
cfg.BoolOpt('sync_data', default=False,
|
||||
help=_("Sync data on connect")),
|
||||
cfg.IntOpt('servertimeout', default=10,
|
||||
cfg.IntOpt('server_timeout', default=10,
|
||||
help=_("Maximum number of seconds to wait for proxy request "
|
||||
"to connect and complete.")),
|
||||
cfg.StrOpt('quantum_id', default='Quantum-' + utils.get_hostname(),
|
||||
help=_("User defined identifier for this Quantum deployment")),
|
||||
cfg.BoolOpt('add_meta_server_route', default=True,
|
||||
help=_("Flag to decide if a route to the metadata server "
|
||||
"should be injected into the VM")),
|
||||
]
|
||||
|
||||
|
||||
@ -88,13 +98,20 @@ cfg.CONF.register_opts(restproxy_opts, "RESTPROXY")
|
||||
# The following are used to invoke the API on the external controller
|
||||
NET_RESOURCE_PATH = "/tenants/%s/networks"
|
||||
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports"
|
||||
ROUTER_RESOURCE_PATH = "/tenants/%s/routers"
|
||||
ROUTER_INTF_OP_PATH = "/tenants/%s/routers/%s/interfaces"
|
||||
NETWORKS_PATH = "/tenants/%s/networks/%s"
|
||||
PORTS_PATH = "/tenants/%s/networks/%s/ports/%s"
|
||||
ATTACHMENT_PATH = "/tenants/%s/networks/%s/ports/%s/attachment"
|
||||
ROUTERS_PATH = "/tenants/%s/routers/%s"
|
||||
ROUTER_INTF_PATH = "/tenants/%s/routers/%s/interfaces/%s"
|
||||
SUCCESS_CODES = range(200, 207)
|
||||
FAILURE_CODES = [0, 301, 302, 303, 400, 401, 403, 404, 500, 501, 502, 503,
|
||||
504, 505]
|
||||
SYNTAX_ERROR_MESSAGE = 'Syntax error in server config file, aborting plugin'
|
||||
BASE_URI = '/networkService/v1.1'
|
||||
ORCHESTRATION_SERVICE_ID = 'Quantum v2.0'
|
||||
METADATA_SERVER_IP = '169.254.169.254'
|
||||
|
||||
|
||||
class RemoteRestError(exceptions.QuantumException):
|
||||
@ -109,7 +126,8 @@ class RemoteRestError(exceptions.QuantumException):
|
||||
class ServerProxy(object):
|
||||
"""REST server proxy to a network controller."""
|
||||
|
||||
def __init__(self, server, port, ssl, auth, timeout, base_uri, name):
|
||||
def __init__(self, server, port, ssl, auth, quantum_id, timeout,
|
||||
base_uri, name):
|
||||
self.server = server
|
||||
self.port = port
|
||||
self.ssl = ssl
|
||||
@ -118,9 +136,11 @@ class ServerProxy(object):
|
||||
self.name = name
|
||||
self.success_codes = SUCCESS_CODES
|
||||
self.auth = None
|
||||
self.quantum_id = quantum_id
|
||||
if auth:
|
||||
self.auth = 'Basic ' + base64.encodestring(auth).strip()
|
||||
|
||||
@lockutils.synchronized('rest_call', 'bsn-', external=True)
|
||||
def rest_call(self, action, resource, data, headers):
|
||||
uri = self.base_uri + resource
|
||||
body = json.dumps(data)
|
||||
@ -129,6 +149,8 @@ class ServerProxy(object):
|
||||
headers['Content-type'] = 'application/json'
|
||||
headers['Accept'] = 'application/json'
|
||||
headers['QuantumProxy-Agent'] = self.name
|
||||
headers['Instance-ID'] = self.quantum_id
|
||||
headers['Orchestration-Service-ID'] = ORCHESTRATION_SERVICE_ID
|
||||
if self.auth:
|
||||
headers['Authorization'] = self.auth
|
||||
|
||||
@ -180,20 +202,21 @@ class ServerProxy(object):
|
||||
|
||||
|
||||
class ServerPool(object):
|
||||
def __init__(self, servers, ssl, auth, timeout=10,
|
||||
def __init__(self, servers, ssl, auth, quantum_id, timeout=10,
|
||||
base_uri='/quantum/v1.0', name='QuantumRestProxy'):
|
||||
self.base_uri = base_uri
|
||||
self.timeout = timeout
|
||||
self.name = name
|
||||
self.auth = auth
|
||||
self.ssl = ssl
|
||||
self.quantum_id = quantum_id
|
||||
self.servers = []
|
||||
for server_port in servers:
|
||||
self.servers.append(self.server_proxy_for(*server_port))
|
||||
|
||||
def server_proxy_for(self, server, port):
|
||||
return ServerProxy(server, port, self.ssl, self.auth, self.timeout,
|
||||
self.base_uri, self.name)
|
||||
return ServerProxy(server, port, self.ssl, self.auth, self.quantum_id,
|
||||
self.timeout, self.base_uri, self.name)
|
||||
|
||||
def server_failure(self, resp):
|
||||
"""Define failure codes as required.
|
||||
@ -254,7 +277,10 @@ class RpcProxy(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
||||
return q_rpc.PluginRpcDispatcher([self])
|
||||
|
||||
|
||||
class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2,
|
||||
l3_db.L3_NAT_db_mixin):
|
||||
|
||||
supported_extension_aliases = ["router"]
|
||||
|
||||
def __init__(self):
|
||||
LOG.info(_('QuantumRestProxy: Starting plugin. Version=%s'),
|
||||
@ -265,12 +291,14 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
|
||||
# 'servers' is the list of network controller REST end-points
|
||||
# (used in order specified till one suceeds, and it is sticky
|
||||
# till next failure). Use 'serverauth' to encode api-key
|
||||
# till next failure). Use 'server_auth' to encode api-key
|
||||
servers = cfg.CONF.RESTPROXY.servers
|
||||
serverauth = cfg.CONF.RESTPROXY.serverauth
|
||||
serverssl = cfg.CONF.RESTPROXY.serverssl
|
||||
syncdata = cfg.CONF.RESTPROXY.syncdata
|
||||
timeout = cfg.CONF.RESTPROXY.servertimeout
|
||||
server_auth = cfg.CONF.RESTPROXY.server_auth
|
||||
server_ssl = cfg.CONF.RESTPROXY.server_ssl
|
||||
sync_data = cfg.CONF.RESTPROXY.sync_data
|
||||
timeout = cfg.CONF.RESTPROXY.server_timeout
|
||||
quantum_id = cfg.CONF.RESTPROXY.quantum_id
|
||||
self.add_meta_server_route = cfg.CONF.RESTPROXY.add_meta_server_route
|
||||
|
||||
# validate config
|
||||
assert servers is not None, 'Servers not defined. Aborting plugin'
|
||||
@ -279,8 +307,8 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
assert all(len(s) == 2 for s in servers), SYNTAX_ERROR_MESSAGE
|
||||
|
||||
# init network ctrl connections
|
||||
self.servers = ServerPool(servers, serverssl, serverauth,
|
||||
timeout)
|
||||
self.servers = ServerPool(servers, server_ssl, server_auth, quantum_id,
|
||||
timeout, BASE_URI)
|
||||
|
||||
# init dhcp support
|
||||
self.topic = topics.PLUGIN
|
||||
@ -291,7 +319,7 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
fanout=False)
|
||||
# Consume from all consumers in a thread
|
||||
self.conn.consume_in_thread()
|
||||
if syncdata:
|
||||
if sync_data:
|
||||
self._send_all_data()
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2: initialization done"))
|
||||
@ -320,26 +348,25 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2: create_network() called"))
|
||||
|
||||
self._warn_on_state_status(network['network'])
|
||||
|
||||
# Validate args
|
||||
tenant_id = self._get_tenant_id_for_create(context, network["network"])
|
||||
net_name = network["network"]["name"]
|
||||
if network["network"]["admin_state_up"] is False:
|
||||
LOG.warning(_("Network with admin_state_up=False are not yet "
|
||||
"supported by this plugin. Ignoring setting for "
|
||||
"network %s"), net_name)
|
||||
|
||||
# create in DB
|
||||
new_net = super(QuantumRestProxyV2, self).create_network(context,
|
||||
network)
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
# create network in DB
|
||||
new_net = super(QuantumRestProxyV2, self).create_network(context,
|
||||
network)
|
||||
self._process_l3_create(context, network['network'], new_net['id'])
|
||||
self._extend_network_dict_l3(context, new_net)
|
||||
|
||||
# create on networl ctrl
|
||||
# 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": {
|
||||
"id": new_net["id"],
|
||||
"name": new_net["name"],
|
||||
}
|
||||
"network": mapped_network
|
||||
}
|
||||
ret = self.servers.post(resource, data)
|
||||
if not self.servers.action_success(ret):
|
||||
@ -379,36 +406,28 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2.update_network() called"))
|
||||
|
||||
# Validate Args
|
||||
if network["network"].get("admin_state_up"):
|
||||
if network["network"]["admin_state_up"] is False:
|
||||
LOG.warning(_("Network with admin_state_up=False are not yet "
|
||||
"supported by this plugin. Ignoring setting for "
|
||||
"network %s", net_name))
|
||||
self._warn_on_state_status(network['network'])
|
||||
|
||||
# update DB
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context, net_id)
|
||||
tenant_id = orig_net["tenant_id"]
|
||||
new_net = super(QuantumRestProxyV2, self).update_network(
|
||||
context, net_id, network)
|
||||
session = context.session
|
||||
with session.begin(subtransactions=True):
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
new_net = super(QuantumRestProxyV2, self).update_network(context,
|
||||
net_id,
|
||||
network)
|
||||
self._process_l3_update(context, network['network'], net_id)
|
||||
self._extend_network_dict_l3(context, new_net)
|
||||
|
||||
# update network on network controller
|
||||
if new_net["name"] != orig_net["name"]:
|
||||
try:
|
||||
resource = NETWORKS_PATH % (tenant_id, net_id)
|
||||
data = {
|
||||
"network": new_net,
|
||||
}
|
||||
ret = self.servers.put(resource, data)
|
||||
if not self.servers.action_success(ret):
|
||||
raise RemoteRestError(ret[2])
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to update remote "
|
||||
"network: %s"), e.message)
|
||||
# reset network to original state
|
||||
super(QuantumRestProxyV2, self).update_network(
|
||||
context, id, orig_net)
|
||||
raise
|
||||
try:
|
||||
self._send_update_network(new_net)
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to update remote "
|
||||
"network: %s"), e.message)
|
||||
# reset network to original state
|
||||
super(QuantumRestProxyV2, self).update_network(context, id,
|
||||
orig_net)
|
||||
raise
|
||||
|
||||
# return updated network
|
||||
return new_net
|
||||
@ -430,6 +449,17 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context, net_id)
|
||||
tenant_id = orig_net["tenant_id"]
|
||||
|
||||
filter = {'network_id': [net_id]}
|
||||
ports = self.get_ports(context, filters=filter)
|
||||
|
||||
# check if there are any tenant owned ports in-use
|
||||
auto_delete_port_owners = db_base_plugin_v2.AUTO_DELETE_PORT_OWNERS
|
||||
only_auto_del = all(p['device_owner'] in auto_delete_port_owners
|
||||
for p in ports)
|
||||
|
||||
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)
|
||||
@ -442,6 +472,7 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: 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
|
||||
@ -477,24 +508,28 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
net = super(QuantumRestProxyV2,
|
||||
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)
|
||||
|
||||
# create on networl ctrl
|
||||
try:
|
||||
resource = PORT_RESOURCE_PATH % (net["tenant_id"], net["id"])
|
||||
mapped_port = self._map_state_and_status(new_port)
|
||||
data = {
|
||||
"port": {
|
||||
"id": new_port["id"],
|
||||
"state": "ACTIVE",
|
||||
}
|
||||
"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
|
||||
if port["port"].get("device_id"):
|
||||
device_id = port["port"].get("device_id")
|
||||
if device_id:
|
||||
self._plug_interface(context,
|
||||
net["tenant_id"], net["id"],
|
||||
new_port["id"], new_port["id"] + "00")
|
||||
new_port["id"], device_id)
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to create remote port: "
|
||||
"%s"), e.message)
|
||||
@ -536,6 +571,8 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
"""
|
||||
LOG.debug(_("QuantumRestProxyV2: update_port() called"))
|
||||
|
||||
self._warn_on_state_status(port['port'])
|
||||
|
||||
# Validate Args
|
||||
orig_port = super(QuantumRestProxyV2, self).get_port(context, port_id)
|
||||
|
||||
@ -547,7 +584,8 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
try:
|
||||
resource = PORTS_PATH % (orig_port["tenant_id"],
|
||||
orig_port["network_id"], port_id)
|
||||
data = {"port": new_port, }
|
||||
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])
|
||||
@ -557,10 +595,11 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
self._unplug_interface(context, orig_port["tenant_id"],
|
||||
orig_port["network_id"],
|
||||
orig_port["id"])
|
||||
if new_port.get("device_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"], new_port["id"] + "00")
|
||||
new_port["id"], device_id)
|
||||
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to create remote port: "
|
||||
@ -573,7 +612,7 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
# return new_port
|
||||
return new_port
|
||||
|
||||
def delete_port(self, context, port_id):
|
||||
def delete_port(self, context, port_id, l3_port_check=True):
|
||||
"""Delete a port.
|
||||
:param context: quantum api request context
|
||||
:param id: UUID representing the port to delete.
|
||||
@ -586,6 +625,15 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2: delete_port() called"))
|
||||
|
||||
# if needed, check to see if this is a port owned by
|
||||
# and l3-router. If so, we should prevent deletion.
|
||||
if l3_port_check:
|
||||
self.prevent_l3_port_deletion(context, port_id)
|
||||
self.disassociate_floatingips(context, port_id)
|
||||
|
||||
super(QuantumRestProxyV2, self).delete_port(context, port_id)
|
||||
|
||||
def _delete_port(self, context, port_id):
|
||||
# Delete from DB
|
||||
port = super(QuantumRestProxyV2, self).get_port(context, port_id)
|
||||
|
||||
@ -600,12 +648,13 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
if port.get("device_id"):
|
||||
self._unplug_interface(context, port["tenant_id"],
|
||||
port["network_id"], port["id"])
|
||||
ret_val = super(QuantumRestProxyV2, self).delete_port(context,
|
||||
port_id)
|
||||
ret_val = super(QuantumRestProxyV2, self)._delete_port(context,
|
||||
port_id)
|
||||
return ret_val
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to update remote port: "
|
||||
"%s"), e.message)
|
||||
raise
|
||||
|
||||
def _plug_interface(self, context, tenant_id, net_id, port_id,
|
||||
remote_interface_id):
|
||||
@ -625,22 +674,6 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
port = super(QuantumRestProxyV2, self).get_port(context, port_id)
|
||||
mac = port["mac_address"]
|
||||
|
||||
for ip in port["fixed_ips"]:
|
||||
if ip.get("subnet_id") is not None:
|
||||
subnet = super(QuantumRestProxyV2, self).get_subnet(
|
||||
context, ip["subnet_id"])
|
||||
gateway = subnet.get("gateway_ip")
|
||||
if gateway is not None:
|
||||
resource = NETWORKS_PATH % (tenant_id, net_id)
|
||||
data = {"network":
|
||||
{"id": net_id,
|
||||
"gateway": gateway,
|
||||
}
|
||||
}
|
||||
ret = self.servers.put(resource, data)
|
||||
if not self.servers.action_success(ret):
|
||||
raise RemoteRestError(ret[2])
|
||||
|
||||
if mac is not None:
|
||||
resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id)
|
||||
data = {"attachment":
|
||||
@ -676,31 +709,326 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to update remote port: "
|
||||
"%s"), e.message)
|
||||
|
||||
def create_subnet(self, context, subnet):
|
||||
LOG.debug(_("QuantumRestProxyV2: create_subnet() called"))
|
||||
|
||||
self._warn_on_state_status(subnet['subnet'])
|
||||
|
||||
# create subnet in DB
|
||||
new_subnet = super(QuantumRestProxyV2, self).create_subnet(context,
|
||||
subnet)
|
||||
net_id = new_subnet['network_id']
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
# rollback creation of subnet
|
||||
super(QuantumRestProxyV2, self).delete_subnet(context,
|
||||
subnet['id'])
|
||||
raise
|
||||
return new_subnet
|
||||
|
||||
def update_subnet(self, context, id, subnet):
|
||||
LOG.debug(_("QuantumRestProxyV2: update_subnet() called"))
|
||||
|
||||
self._warn_on_state_status(subnet['subnet'])
|
||||
|
||||
orig_subnet = super(QuantumRestProxyV2, self)._get_subnet(context, id)
|
||||
|
||||
# update subnet in DB
|
||||
new_subnet = super(QuantumRestProxyV2, self).update_subnet(context, id,
|
||||
subnet)
|
||||
net_id = new_subnet['network_id']
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
# rollback updation of subnet
|
||||
super(QuantumRestProxyV2, self).update_subnet(context, id,
|
||||
orig_subnet)
|
||||
raise
|
||||
return new_subnet
|
||||
|
||||
def delete_subnet(self, context, id):
|
||||
LOG.debug(_("QuantumRestProxyV2: delete_subnet() called"))
|
||||
orig_subnet = super(QuantumRestProxyV2, self).get_subnet(context, id)
|
||||
net_id = orig_subnet['network_id']
|
||||
# delete subnet in DB
|
||||
super(QuantumRestProxyV2, self).delete_subnet(context, id)
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
# TODO (Sumit): rollback deletion of subnet
|
||||
raise
|
||||
|
||||
def create_router(self, context, router):
|
||||
LOG.debug(_("QuantumRestProxyV2: create_router() called"))
|
||||
|
||||
self._warn_on_state_status(router['router'])
|
||||
|
||||
tenant_id = self._get_tenant_id_for_create(context, router["router"])
|
||||
|
||||
# create router in DB
|
||||
new_router = super(QuantumRestProxyV2, 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)
|
||||
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(_("QuantumRestProxyV2: Unable to create remote router: "
|
||||
"%s"), e.message)
|
||||
super(QuantumRestProxyV2, self).delete_router(context,
|
||||
new_router['id'])
|
||||
raise
|
||||
|
||||
# return created router
|
||||
return new_router
|
||||
|
||||
def update_router(self, context, router_id, router):
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2.update_router() called"))
|
||||
|
||||
self._warn_on_state_status(router['router'])
|
||||
|
||||
orig_router = super(QuantumRestProxyV2, self).get_router(context,
|
||||
router_id)
|
||||
tenant_id = orig_router["tenant_id"]
|
||||
new_router = super(QuantumRestProxyV2, self).update_router(context,
|
||||
router_id,
|
||||
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(_("QuantumRestProxyV2: Unable to update remote router: "
|
||||
"%s"), e.message)
|
||||
# reset router to original state
|
||||
super(QuantumRestProxyV2, self).update_router(context,
|
||||
router_id,
|
||||
orig_router)
|
||||
raise
|
||||
|
||||
# return updated router
|
||||
return new_router
|
||||
|
||||
def delete_router(self, context, router_id):
|
||||
LOG.debug(_("QuantumRestProxyV2: delete_router() called"))
|
||||
|
||||
with context.session.begin(subtransactions=True):
|
||||
orig_router = self._get_router(context, router_id)
|
||||
tenant_id = orig_router["tenant_id"]
|
||||
|
||||
# Ensure that the router is not used
|
||||
router_filter = {'router_id': [router_id]}
|
||||
fips = self.get_floatingips_count(context.elevated(),
|
||||
filters=router_filter)
|
||||
if fips:
|
||||
raise l3.RouterInUse(router_id=router_id)
|
||||
|
||||
device_owner = l3_db.DEVICE_OWNER_ROUTER_INTF
|
||||
device_filter = {'device_id': [router_id],
|
||||
'device_owner': [device_owner]}
|
||||
ports = self.get_ports_count(context.elevated(),
|
||||
filters=device_filter)
|
||||
if ports:
|
||||
raise l3.RouterInUse(router_id=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(QuantumRestProxyV2, self).delete_router(context,
|
||||
router_id)
|
||||
return ret_val
|
||||
except RemoteRestError as e:
|
||||
LOG.error(_("QuantumRestProxyV2: Unable to delete remote router: "
|
||||
"%s"), e.message)
|
||||
raise
|
||||
|
||||
def add_router_interface(self, context, router_id, interface_info):
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2: add_router_interface() called"))
|
||||
|
||||
# Validate args
|
||||
router = self._get_router(context, router_id)
|
||||
tenant_id = router['tenant_id']
|
||||
|
||||
# create interface in DB
|
||||
new_interface_info = super(QuantumRestProxyV2,
|
||||
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)
|
||||
|
||||
# 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(_("QuantumRestProxyV2: Unable to create interface: "
|
||||
"%s"), e.message)
|
||||
super(QuantumRestProxyV2,
|
||||
self).remove_router_interface(context, router_id,
|
||||
interface_info)
|
||||
raise
|
||||
|
||||
return new_interface_info
|
||||
|
||||
def remove_router_interface(self, context, router_id, interface_info):
|
||||
|
||||
LOG.debug(_("QuantumRestProxyV2: remove_router_interface() called"))
|
||||
|
||||
# Validate args
|
||||
router = self._get_router(context, router_id)
|
||||
tenant_id = router['tenant_id']
|
||||
|
||||
# we will first get the interface identifier before deleting in the DB
|
||||
if not interface_info:
|
||||
msg = "Either subnet_id or port_id must be specified"
|
||||
raise exceptions.BadRequest(resource='router', msg=msg)
|
||||
if 'port_id' in interface_info:
|
||||
port = self._get_port(context, interface_info['port_id'])
|
||||
interface_id = port['network_id']
|
||||
elif 'subnet_id' in interface_info:
|
||||
subnet = self._get_subnet(context, interface_info['subnet_id'])
|
||||
interface_id = subnet['network_id']
|
||||
else:
|
||||
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(QuantumRestProxyV2,
|
||||
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(_("QuantumRestProxyV2:Unable to delete remote intf: "
|
||||
"%s"), e.message)
|
||||
raise
|
||||
|
||||
# return new interface
|
||||
return del_intf_info
|
||||
|
||||
def create_floatingip(self, context, floatingip):
|
||||
LOG.debug(_("QuantumRestProxyV2: create_floatingip() called"))
|
||||
|
||||
# create floatingip in DB
|
||||
new_fl_ip = super(QuantumRestProxyV2,
|
||||
self).create_floatingip(context, floatingip)
|
||||
|
||||
net_id = new_fl_ip['floating_network_id']
|
||||
orig_net = super(QuantumRestProxyV2, 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(_("QuantumRestProxyV2: Unable to create remote "
|
||||
"floatin IP: %s"), e.message)
|
||||
super(QuantumRestProxyV2, self).delete_floatingip(context,
|
||||
floatingip)
|
||||
raise
|
||||
|
||||
# return created floating IP
|
||||
return new_fl_ip
|
||||
|
||||
def update_floatingip(self, context, id, floatingip):
|
||||
LOG.debug(_("QuantumRestProxyV2: update_floatingip() called"))
|
||||
|
||||
orig_fl_ip = super(QuantumRestProxyV2, self).get_floatingip(context,
|
||||
id)
|
||||
|
||||
# update floatingip in DB
|
||||
new_fl_ip = super(QuantumRestProxyV2,
|
||||
self).update_floatingip(context, id, floatingip)
|
||||
|
||||
net_id = new_fl_ip['floating_network_id']
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
# rollback updation of subnet
|
||||
super(QuantumRestProxyV2, self).update_floatingip(context, id,
|
||||
orig_fl_ip)
|
||||
raise
|
||||
return new_fl_ip
|
||||
|
||||
def delete_floatingip(self, context, id):
|
||||
LOG.debug(_("QuantumRestProxyV2: delete_floatingip() called"))
|
||||
|
||||
orig_fl_ip = super(QuantumRestProxyV2, self).get_floatingip(context,
|
||||
id)
|
||||
# delete floating IP in DB
|
||||
net_id = orig_fl_ip['floating_network_id']
|
||||
super(QuantumRestProxyV2, self).delete_floatingip(context, id)
|
||||
|
||||
orig_net = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
# update network on network controller
|
||||
try:
|
||||
self._send_update_network(orig_net)
|
||||
except RemoteRestError as e:
|
||||
# TODO(Sumit): rollback deletion of floating IP
|
||||
raise
|
||||
|
||||
def _send_all_data(self):
|
||||
"""Pushes all data to network ctrl (networks/ports, ports/attachments)
|
||||
to give the controller an option to re-sync it's persistent store
|
||||
with quantum's current view of that data.
|
||||
"""
|
||||
admin_context = qcontext.get_admin_context()
|
||||
networks = {}
|
||||
ports = {}
|
||||
networks = []
|
||||
routers = []
|
||||
|
||||
all_networks = super(QuantumRestProxyV2,
|
||||
self).get_networks(admin_context) or []
|
||||
for net in all_networks:
|
||||
networks[net.get('id')] = {
|
||||
'id': net.get('id'),
|
||||
'name': net.get('name'),
|
||||
'op-status': net.get('admin_state_up'),
|
||||
}
|
||||
|
||||
subnets = net.get('subnets', [])
|
||||
for subnet_id in subnets:
|
||||
subnet = self.get_subnet(admin_context, subnet_id)
|
||||
gateway_ip = subnet.get('gateway_ip')
|
||||
if gateway_ip:
|
||||
# FIX: For backward compatibility with wire protocol
|
||||
networks[net.get('id')]['gateway'] = gateway_ip
|
||||
mapped_network = self._get_mapped_network_with_subnets(net)
|
||||
net_fl_ips = self._get_network_with_floatingips(mapped_network)
|
||||
|
||||
ports = []
|
||||
net_filter = {'network_id': [net.get('id')]}
|
||||
@ -708,28 +1036,166 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
self).get_ports(admin_context,
|
||||
filters=net_filter) or []
|
||||
for port in net_ports:
|
||||
port_details = {
|
||||
'id': port.get('id'),
|
||||
'attachment': {
|
||||
'id': port.get('id') + '00',
|
||||
'mac': port.get('mac_address'),
|
||||
},
|
||||
'state': port.get('status'),
|
||||
'op-status': port.get('admin_state_up'),
|
||||
'mac': None
|
||||
mapped_port = self._map_state_and_status(port)
|
||||
mapped_port['attachment'] = {
|
||||
'id': port.get('device_id'),
|
||||
'mac': port.get('mac_address'),
|
||||
}
|
||||
ports.append(port_details)
|
||||
networks[net.get('id')]['ports'] = ports
|
||||
ports.append(mapped_port)
|
||||
net_fl_ips['ports'] = ports
|
||||
|
||||
networks.append(net_fl_ips)
|
||||
|
||||
all_routers = super(QuantumRestProxyV2,
|
||||
self).get_routers(admin_context) or []
|
||||
for router in all_routers:
|
||||
interfaces = []
|
||||
mapped_router = self._map_state_and_status(router)
|
||||
router_filter = {
|
||||
'device_owner': ["network:router_interface"],
|
||||
'device_id': [router.get('id')]
|
||||
}
|
||||
router_ports = super(QuantumRestProxyV2,
|
||||
self).get_ports(admin_context,
|
||||
filters=router_filter) or []
|
||||
for port in router_ports:
|
||||
net_id = port.get('network_id')
|
||||
subnet_id = port['fixed_ips'][0]['subnet_id']
|
||||
intf_details = self._get_router_intf_details(admin_context,
|
||||
net_id,
|
||||
subnet_id)
|
||||
interfaces.append(intf_details)
|
||||
mapped_router['interfaces'] = interfaces
|
||||
|
||||
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(_('QuantumRestProxy: Unable to update remote network: '
|
||||
'%s'), e.message)
|
||||
LOG.error(_('QuantumRestProxy: Unable to update remote '
|
||||
'topology: %s'), e.message)
|
||||
raise
|
||||
|
||||
def _add_host_route(self, context, destination, port):
|
||||
subnet = {}
|
||||
for fixed_ip in port['fixed_ips']:
|
||||
subnet_id = fixed_ip['subnet_id']
|
||||
nexthop = fixed_ip['ip_address']
|
||||
subnet['host_routes'] = [{'destination': destination,
|
||||
'nexthop': nexthop}]
|
||||
self.update_subnet(context, subnet_id, {'subnet': subnet})
|
||||
LOG.debug(_("Adding host route: "))
|
||||
LOG.debug(_("destination:%s nexthop:%s"), (destination,
|
||||
nexthop))
|
||||
|
||||
def _get_network_with_floatingips(self, network):
|
||||
admin_context = qcontext.get_admin_context()
|
||||
|
||||
net_id = network['id']
|
||||
net_filter = {'floating_network_id': [net_id]}
|
||||
fl_ips = super(QuantumRestProxyV2,
|
||||
self).get_floatingips(admin_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)
|
||||
subnets_details = []
|
||||
if subnets:
|
||||
for subnet in subnets:
|
||||
subnet_dict = self._make_subnet_dict(subnet)
|
||||
mapped_subnet = self._map_state_and_status(subnet_dict)
|
||||
subnets_details.append(mapped_subnet)
|
||||
|
||||
return subnets_details
|
||||
|
||||
def _get_mapped_network_with_subnets(self, network):
|
||||
admin_context = qcontext.get_admin_context()
|
||||
network = self._map_state_and_status(network)
|
||||
subnets = self._get_all_subnets_json_for_network(network['id'])
|
||||
network['subnets'] = subnets
|
||||
for subnet in (subnets or []):
|
||||
if subnet['gateway_ip']:
|
||||
# FIX: For backward compatibility with wire protocol
|
||||
network['gateway'] = subnet['gateway_ip']
|
||||
break
|
||||
else:
|
||||
network['gateway'] = ''
|
||||
|
||||
network[l3.EXTERNAL] = self._network_is_external(admin_context,
|
||||
network['id'])
|
||||
|
||||
return network
|
||||
|
||||
def _send_update_network(self, network):
|
||||
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(_("QuantumRestProxyV2: Unable to update remote "
|
||||
"network: %s"), e.message)
|
||||
raise
|
||||
|
||||
def _map_state_and_status(self, resource):
|
||||
resource = copy.copy(resource)
|
||||
|
||||
resource['state'] = ('UP' if resource.pop('admin_state_up',
|
||||
True) else 'DOWN')
|
||||
|
||||
if 'status' in resource:
|
||||
del resource['status']
|
||||
|
||||
return resource
|
||||
|
||||
def _warn_on_state_status(self, resource):
|
||||
if resource.get('admin_state_up', True) is False:
|
||||
LOG.warning(_("Setting admin_state_up=False is not supported"
|
||||
" in this plugin version. Ignoring setting for "
|
||||
"resource: %s"), resource)
|
||||
|
||||
if 'status' in resource:
|
||||
if resource['status'] is not const.NET_STATUS_ACTIVE:
|
||||
LOG.warning(_("Operational status is internally set by the"
|
||||
" plugin. Ignoring setting status=%s."),
|
||||
resource['status'])
|
||||
|
||||
def _get_router_intf_details(self, context, intf_id, subnet_id):
|
||||
|
||||
# we will use the network id as interface's id
|
||||
net_id = intf_id
|
||||
network = super(QuantumRestProxyV2, self).get_network(context,
|
||||
net_id)
|
||||
subnet = super(QuantumRestProxyV2, self).get_subnet(context,
|
||||
subnet_id)
|
||||
mapped_network = self._get_mapped_network_with_subnets(network)
|
||||
mapped_subnet = self._map_state_and_status(subnet)
|
||||
|
||||
data = {
|
||||
'id': intf_id,
|
||||
"network": mapped_network,
|
||||
"subnet": mapped_subnet
|
||||
}
|
||||
|
||||
return data
|
||||
|
27
quantum/plugins/bigswitch/vcsversion.py
Normal file
27
quantum/plugins/bigswitch/vcsversion.py
Normal file
@ -0,0 +1,27 @@
|
||||
# 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: Sumit Naiksatam, sumitnaiksatam@gmail.com
|
||||
#
|
||||
version_info = {'branch_nick': u'quantum/trunk',
|
||||
'revision_id': u'1',
|
||||
'revno': 0}
|
||||
|
||||
|
||||
QUANTUMRESTPROXY_VERSION = ['2013', '1', None]
|
||||
|
||||
|
||||
FINAL = False # This becomes true at Release Candidate time
|
@ -23,16 +23,22 @@
|
||||
|
||||
# if vcsversion exists, use it. Else, use LOCALBRANCH:LOCALREVISION
|
||||
try:
|
||||
from bigswitch.vcsversion import version_info
|
||||
from quantum.plugins.bigswitch.vcsversion import version_info
|
||||
except ImportError:
|
||||
version_info = {'branch_nick': u'LOCALBRANCH',
|
||||
'revision_id': u'LOCALREVISION',
|
||||
'revno': 0}
|
||||
try:
|
||||
from quantum.plugins.bigswitch.vcsversion import QUANTUMRESTPROXY_VERSION
|
||||
except ImportError:
|
||||
QUANTUMRESTPROXY_VERSION = ['2013', '1', None]
|
||||
try:
|
||||
from quantum.plugins.bigswitch.vcsversion import FINAL
|
||||
except ImportError:
|
||||
FINAL = False # This becomes true at Release Candidate time
|
||||
|
||||
|
||||
QUANTUMRESTPROXY_VERSION = ['2012', '1', None]
|
||||
YEAR, COUNT, REVISION = QUANTUMRESTPROXY_VERSION
|
||||
FINAL = False # This becomes true at Release Candidate time
|
||||
|
||||
|
||||
def canonical_version_string():
|
||||
|
317
quantum/tests/unit/bigswitch/test_router_db.py
Normal file
317
quantum/tests/unit/bigswitch/test_router_db.py
Normal file
@ -0,0 +1,317 @@
|
||||
# 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.
|
||||
#
|
||||
# Adapted from quantum.tests.unit.test_l3_plugin
|
||||
# @author: Sumit Naiksatam, sumitnaiksatam@gmail.com
|
||||
#
|
||||
|
||||
import os
|
||||
|
||||
from mock import patch
|
||||
from webob import exc
|
||||
|
||||
from quantum.common.test_lib import test_config
|
||||
from quantum.extensions import l3
|
||||
from quantum.manager import QuantumManager
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.openstack.common.notifier import api as notifier_api
|
||||
from quantum.openstack.common.notifier import test_notifier
|
||||
from quantum.tests.unit import test_l3_plugin
|
||||
|
||||
|
||||
def new_L3_setUp(self):
|
||||
test_config['plugin_name_v2'] = (
|
||||
'quantum.plugins.bigswitch.plugin.QuantumRestProxyV2')
|
||||
etc_path = os.path.join(os.path.dirname(__file__), 'etc')
|
||||
rp_conf_file = os.path.join(etc_path, 'restproxy.ini.test')
|
||||
test_config['config_files'] = [rp_conf_file]
|
||||
cfg.CONF.set_default('allow_overlapping_ips', False)
|
||||
ext_mgr = L3TestExtensionManager()
|
||||
test_config['extension_manager'] = ext_mgr
|
||||
super(test_l3_plugin.L3NatDBTestCase, self).setUp()
|
||||
|
||||
# Set to None to reload the drivers
|
||||
notifier_api._drivers = None
|
||||
cfg.CONF.set_override("notification_driver", [test_notifier.__name__])
|
||||
|
||||
|
||||
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 HTTPConnectionMock():
|
||||
|
||||
def __init__(self, server, port, timeout):
|
||||
pass
|
||||
|
||||
def request(self, action, uri, body, headers):
|
||||
return
|
||||
|
||||
def getresponse(self):
|
||||
return HTTPResponseMock(None)
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
|
||||
class L3TestExtensionManager(object):
|
||||
|
||||
def get_resources(self):
|
||||
return l3.L3.get_resources()
|
||||
|
||||
def get_actions(self):
|
||||
return []
|
||||
|
||||
def get_request_extensions(self):
|
||||
return []
|
||||
|
||||
|
||||
class RouterDBTestCase(test_l3_plugin.L3NatDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.httpPatch = patch('httplib.HTTPConnection', create=True,
|
||||
new=HTTPConnectionMock)
|
||||
self.httpPatch.start()
|
||||
test_l3_plugin.L3NatDBTestCase.setUp = new_L3_setUp
|
||||
super(RouterDBTestCase, self).setUp()
|
||||
self.plugin_obj = QuantumManager.get_plugin()
|
||||
|
||||
def tearDown(self):
|
||||
self.httpPatch.stop()
|
||||
super(RouterDBTestCase, self).tearDown()
|
||||
del test_config['plugin_name_v2']
|
||||
del test_config['config_files']
|
||||
cfg.CONF.reset()
|
||||
test_l3_plugin.L3NatDBTestCase.setUp = origSetUp
|
||||
|
||||
def test_router_remove_router_interface_wrong_subnet_returns_409(self):
|
||||
with self.router() as r:
|
||||
with self.subnet() as s:
|
||||
with self.subnet(cidr='10.0.10.0/24') as s1:
|
||||
with self.port(subnet=s1, no_delete=True) as p:
|
||||
self._router_interface_action('add',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
s['subnet']['id'],
|
||||
p['port']['id'],
|
||||
exc.HTTPConflict.code)
|
||||
#remove properly to clean-up
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
|
||||
def test_router_remove_router_interface_wrong_port_returns_404(self):
|
||||
with self.router() as r:
|
||||
with self.subnet() as s:
|
||||
with self.port(subnet=s, no_delete=True) as p:
|
||||
self._router_interface_action('add',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
# create another port for testing failure case
|
||||
res = self._create_port('json', p['port']['network_id'])
|
||||
p2 = self.deserialize('json', res)
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p2['port']['id'],
|
||||
exc.HTTPNotFound.code)
|
||||
# remove correct interface to cleanup
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
# remove extra port created
|
||||
self._delete('ports', p2['port']['id'])
|
||||
|
||||
def test_create_floatingip_no_ext_gateway_return_404(self):
|
||||
with self.subnet(cidr='10.0.10.0/24') as public_sub:
|
||||
self._set_net_external(public_sub['subnet']['network_id'])
|
||||
with self.port() as private_port:
|
||||
with self.router() as r:
|
||||
res = self._create_floatingip(
|
||||
'json',
|
||||
public_sub['subnet']['network_id'],
|
||||
port_id=private_port['port']['id'])
|
||||
self.assertEqual(res.status_int, exc.HTTPNotFound.code)
|
||||
|
||||
def test_router_update_gateway(self):
|
||||
with self.router() as r:
|
||||
with self.subnet() as s1:
|
||||
with self.subnet(cidr='10.0.10.0/24') as s2:
|
||||
self._set_net_external(s1['subnet']['network_id'])
|
||||
self._add_external_gateway_to_router(
|
||||
r['router']['id'],
|
||||
s1['subnet']['network_id'])
|
||||
body = self._show('routers', r['router']['id'])
|
||||
net_id = (body['router']
|
||||
['external_gateway_info']['network_id'])
|
||||
self.assertEquals(net_id, s1['subnet']['network_id'])
|
||||
self._set_net_external(s2['subnet']['network_id'])
|
||||
self._add_external_gateway_to_router(
|
||||
r['router']['id'],
|
||||
s2['subnet']['network_id'])
|
||||
body = self._show('routers', r['router']['id'])
|
||||
net_id = (body['router']
|
||||
['external_gateway_info']['network_id'])
|
||||
self.assertEquals(net_id, s2['subnet']['network_id'])
|
||||
self._remove_external_gateway_from_router(
|
||||
r['router']['id'],
|
||||
s2['subnet']['network_id'])
|
||||
|
||||
def test_router_add_interface_overlapped_cidr(self):
|
||||
self.skipTest("Plugin does not support")
|
||||
|
||||
def test_router_add_interface_overlapped_cidr_returns_400(self):
|
||||
self.skipTest("Plugin does not support")
|
||||
|
||||
def test_list_nets_external(self):
|
||||
self.skipTest("Plugin does not support")
|
||||
|
||||
def test_router_update_gateway_with_existed_floatingip(self):
|
||||
with self.subnet(cidr='10.0.10.0/24') as subnet:
|
||||
self._set_net_external(subnet['subnet']['network_id'])
|
||||
with self.floatingip_with_assoc() as fip:
|
||||
self._add_external_gateway_to_router(
|
||||
fip['floatingip']['router_id'],
|
||||
subnet['subnet']['network_id'],
|
||||
expected_code=exc.HTTPConflict.code)
|
||||
|
||||
def test_router_remove_interface_wrong_subnet_returns_409(self):
|
||||
with self.router() as r:
|
||||
with self.subnet(cidr='10.0.10.0/24') as s:
|
||||
with self.port(no_delete=True) as p:
|
||||
self._router_interface_action('add',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
s['subnet']['id'],
|
||||
p['port']['id'],
|
||||
exc.HTTPConflict.code)
|
||||
#remove properly to clean-up
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
|
||||
def test_router_remove_interface_wrong_port_returns_404(self):
|
||||
with self.router() as r:
|
||||
with self.subnet(cidr='10.0.10.0/24') as s:
|
||||
with self.port(no_delete=True) as p:
|
||||
self._router_interface_action('add',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
# create another port for testing failure case
|
||||
res = self._create_port('json', p['port']['network_id'])
|
||||
p2 = self.deserialize('json', res)
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p2['port']['id'],
|
||||
exc.HTTPNotFound.code)
|
||||
# remove correct interface to cleanup
|
||||
self._router_interface_action('remove',
|
||||
r['router']['id'],
|
||||
None,
|
||||
p['port']['id'])
|
||||
# remove extra port created
|
||||
self._delete('ports', p2['port']['id'])
|
||||
|
||||
def test_send_data(self):
|
||||
fmt = 'json'
|
||||
plugin_obj = QuantumManager.get_plugin()
|
||||
|
||||
with self.router() as r:
|
||||
r_id = r['router']['id']
|
||||
|
||||
with self.subnet(cidr='10.0.10.0/24') as s:
|
||||
s_id = s['subnet']['id']
|
||||
|
||||
with self.router() as r1:
|
||||
r1_id = r1['router']['id']
|
||||
body = self._router_interface_action('add', r_id, s_id,
|
||||
None)
|
||||
self.assertTrue('port_id' in body)
|
||||
r_port_id = body['port_id']
|
||||
body = self._show('ports', r_port_id)
|
||||
self.assertEquals(body['port']['device_id'], r_id)
|
||||
|
||||
with self.subnet(cidr='10.0.20.0/24') as s1:
|
||||
s1_id = s1['subnet']['id']
|
||||
body = self._router_interface_action('add', r1_id,
|
||||
s1_id, None)
|
||||
self.assertTrue('port_id' in body)
|
||||
r1_port_id = body['port_id']
|
||||
body = self._show('ports', r1_port_id)
|
||||
self.assertEquals(body['port']['device_id'], r1_id)
|
||||
|
||||
with self.subnet(cidr='11.0.0.0/24') as public_sub:
|
||||
public_net_id = public_sub['subnet']['network_id']
|
||||
self._set_net_external(public_net_id)
|
||||
|
||||
with self.port() as prv_port:
|
||||
prv_fixed_ip = prv_port['port']['fixed_ips'][0]
|
||||
priv_sub_id = prv_fixed_ip['subnet_id']
|
||||
self._add_external_gateway_to_router(
|
||||
r_id, public_net_id)
|
||||
self._router_interface_action('add', r_id,
|
||||
priv_sub_id,
|
||||
None)
|
||||
|
||||
priv_port_id = prv_port['port']['id']
|
||||
res = self._create_floatingip(
|
||||
fmt, public_net_id,
|
||||
port_id=priv_port_id)
|
||||
self.assertEqual(res.status_int,
|
||||
exc.HTTPCreated.code)
|
||||
floatingip = self.deserialize(fmt, res)
|
||||
|
||||
result = plugin_obj._send_all_data()
|
||||
self.assertEquals(result[0], 200)
|
||||
|
||||
self._delete('floatingips',
|
||||
floatingip['floatingip']['id'])
|
||||
self._remove_external_gateway_from_router(
|
||||
r_id, public_net_id)
|
||||
self._router_interface_action('remove', r_id,
|
||||
priv_sub_id,
|
||||
None)
|
||||
self._router_interface_action('remove', r_id, s_id,
|
||||
None)
|
||||
self._show('ports', r_port_id,
|
||||
expected_code=exc.HTTPNotFound.code)
|
||||
self._router_interface_action('remove', r1_id, s1_id,
|
||||
None)
|
||||
self._show('ports', r1_port_id,
|
||||
expected_code=exc.HTTPNotFound.code)
|
Loading…
Reference in New Issue
Block a user