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:
Sumit Naiksatam 2013-02-05 20:25:46 -08:00
parent df77f43b26
commit 489f6ec97e
5 changed files with 942 additions and 126 deletions

View File

@ -28,13 +28,13 @@ reconnect_interval = 2
# #
# The following parameters are supported: # The following parameters are supported:
# servers : <host:port>[,<host:port>]* (Error if not set) # servers : <host:port>[,<host:port>]* (Error if not set)
# serverauth : <username:password> (default: no auth) # server_auth : <username:password> (default: no auth)
# serverssl : True | False (default: False) # server_ssl : True | False (default: False)
# syncdata : True | False (default: False) # sync_data : True | False (default: False)
# servertimeout : 10 (default: 10 seconds) # server_timeout : 10 (default: 10 seconds)
# #
servers=localhost:8080 servers=localhost:8080
#serverauth=username:password #server_auth=username:password
#serverssl=True #server_ssl=True
#syncdata=True #sync_data=True
#servertimeout=10 #server_timeout=10

View File

@ -45,18 +45,24 @@ on port-attach) on an additional PUT to do a bulk dump of all persistent data.
""" """
import base64 import base64
import copy
import httplib import httplib
import json import json
import socket import socket
from quantum.common import constants as const
from quantum.common import exceptions from quantum.common import exceptions
from quantum.common import rpc as q_rpc from quantum.common import rpc as q_rpc
from quantum.common import topics from quantum.common import topics
from quantum.common import utils
from quantum import context as qcontext from quantum import context as qcontext
from quantum.db import api as db from quantum.db import api as db
from quantum.db import db_base_plugin_v2 from quantum.db import db_base_plugin_v2
from quantum.db import dhcp_rpc_base 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 cfg
from quantum.openstack.common import lockutils
from quantum.openstack.common import log as logging from quantum.openstack.common import log as logging
from quantum.openstack.common import rpc from quantum.openstack.common import rpc
from quantum.plugins.bigswitch.version import version_string_with_vcs from quantum.plugins.bigswitch.version import version_string_with_vcs
@ -69,16 +75,20 @@ restproxy_opts = [
cfg.StrOpt('servers', default='localhost:8800', cfg.StrOpt('servers', default='localhost:8800',
help=_("A comma separated list of servers and port numbers " help=_("A comma separated list of servers and port numbers "
"to proxy request to.")), "to proxy request to.")),
cfg.StrOpt('serverauth', default='username:password', cfg.StrOpt('server_auth', default='username:password', secret=True,
help=_("Server authentication"), help=_("Server authentication")),
secret=True), cfg.BoolOpt('server_ssl', default=False,
cfg.BoolOpt('serverssl', default=False,
help=_("Use SSL to connect")), help=_("Use SSL to connect")),
cfg.BoolOpt('syncdata', default=False, cfg.BoolOpt('sync_data', default=False,
help=_("Sync data on connect")), 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 " help=_("Maximum number of seconds to wait for proxy request "
"to connect and complete.")), "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 # The following are used to invoke the API on the external controller
NET_RESOURCE_PATH = "/tenants/%s/networks" NET_RESOURCE_PATH = "/tenants/%s/networks"
PORT_RESOURCE_PATH = "/tenants/%s/networks/%s/ports" 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" NETWORKS_PATH = "/tenants/%s/networks/%s"
PORTS_PATH = "/tenants/%s/networks/%s/ports/%s" PORTS_PATH = "/tenants/%s/networks/%s/ports/%s"
ATTACHMENT_PATH = "/tenants/%s/networks/%s/ports/%s/attachment" 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) SUCCESS_CODES = range(200, 207)
FAILURE_CODES = [0, 301, 302, 303, 400, 401, 403, 404, 500, 501, 502, 503, FAILURE_CODES = [0, 301, 302, 303, 400, 401, 403, 404, 500, 501, 502, 503,
504, 505] 504, 505]
SYNTAX_ERROR_MESSAGE = 'Syntax error in server config file, aborting plugin' 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): class RemoteRestError(exceptions.QuantumException):
@ -109,7 +126,8 @@ class RemoteRestError(exceptions.QuantumException):
class ServerProxy(object): class ServerProxy(object):
"""REST server proxy to a network controller.""" """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.server = server
self.port = port self.port = port
self.ssl = ssl self.ssl = ssl
@ -118,9 +136,11 @@ class ServerProxy(object):
self.name = name self.name = name
self.success_codes = SUCCESS_CODES self.success_codes = SUCCESS_CODES
self.auth = None self.auth = None
self.quantum_id = quantum_id
if auth: if auth:
self.auth = 'Basic ' + base64.encodestring(auth).strip() self.auth = 'Basic ' + base64.encodestring(auth).strip()
@lockutils.synchronized('rest_call', 'bsn-', external=True)
def rest_call(self, action, resource, data, headers): def rest_call(self, action, resource, data, headers):
uri = self.base_uri + resource uri = self.base_uri + resource
body = json.dumps(data) body = json.dumps(data)
@ -129,6 +149,8 @@ class ServerProxy(object):
headers['Content-type'] = 'application/json' headers['Content-type'] = 'application/json'
headers['Accept'] = 'application/json' headers['Accept'] = 'application/json'
headers['QuantumProxy-Agent'] = self.name headers['QuantumProxy-Agent'] = self.name
headers['Instance-ID'] = self.quantum_id
headers['Orchestration-Service-ID'] = ORCHESTRATION_SERVICE_ID
if self.auth: if self.auth:
headers['Authorization'] = self.auth headers['Authorization'] = self.auth
@ -180,20 +202,21 @@ class ServerProxy(object):
class ServerPool(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'): base_uri='/quantum/v1.0', name='QuantumRestProxy'):
self.base_uri = base_uri self.base_uri = base_uri
self.timeout = timeout self.timeout = timeout
self.name = name self.name = name
self.auth = auth self.auth = auth
self.ssl = ssl self.ssl = ssl
self.quantum_id = quantum_id
self.servers = [] self.servers = []
for server_port in servers: for server_port in servers:
self.servers.append(self.server_proxy_for(*server_port)) self.servers.append(self.server_proxy_for(*server_port))
def server_proxy_for(self, server, port): def server_proxy_for(self, server, port):
return ServerProxy(server, port, self.ssl, self.auth, self.timeout, return ServerProxy(server, port, self.ssl, self.auth, self.quantum_id,
self.base_uri, self.name) self.timeout, self.base_uri, self.name)
def server_failure(self, resp): def server_failure(self, resp):
"""Define failure codes as required. """Define failure codes as required.
@ -254,7 +277,10 @@ class RpcProxy(dhcp_rpc_base.DhcpRpcCallbackMixin):
return q_rpc.PluginRpcDispatcher([self]) 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): def __init__(self):
LOG.info(_('QuantumRestProxy: Starting plugin. Version=%s'), 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 # 'servers' is the list of network controller REST end-points
# (used in order specified till one suceeds, and it is sticky # (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 servers = cfg.CONF.RESTPROXY.servers
serverauth = cfg.CONF.RESTPROXY.serverauth server_auth = cfg.CONF.RESTPROXY.server_auth
serverssl = cfg.CONF.RESTPROXY.serverssl server_ssl = cfg.CONF.RESTPROXY.server_ssl
syncdata = cfg.CONF.RESTPROXY.syncdata sync_data = cfg.CONF.RESTPROXY.sync_data
timeout = cfg.CONF.RESTPROXY.servertimeout 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 # validate config
assert servers is not None, 'Servers not defined. Aborting plugin' 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 assert all(len(s) == 2 for s in servers), SYNTAX_ERROR_MESSAGE
# init network ctrl connections # init network ctrl connections
self.servers = ServerPool(servers, serverssl, serverauth, self.servers = ServerPool(servers, server_ssl, server_auth, quantum_id,
timeout) timeout, BASE_URI)
# init dhcp support # init dhcp support
self.topic = topics.PLUGIN self.topic = topics.PLUGIN
@ -291,7 +319,7 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
fanout=False) fanout=False)
# Consume from all consumers in a thread # Consume from all consumers in a thread
self.conn.consume_in_thread() self.conn.consume_in_thread()
if syncdata: if sync_data:
self._send_all_data() self._send_all_data()
LOG.debug(_("QuantumRestProxyV2: initialization done")) LOG.debug(_("QuantumRestProxyV2: initialization done"))
@ -320,26 +348,25 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
LOG.debug(_("QuantumRestProxyV2: create_network() called")) LOG.debug(_("QuantumRestProxyV2: create_network() called"))
self._warn_on_state_status(network['network'])
# Validate args # Validate args
tenant_id = self._get_tenant_id_for_create(context, network["network"]) 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 session = context.session
with session.begin(subtransactions=True):
# create network in DB
new_net = super(QuantumRestProxyV2, self).create_network(context, new_net = super(QuantumRestProxyV2, self).create_network(context,
network) 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: try:
resource = NET_RESOURCE_PATH % tenant_id resource = NET_RESOURCE_PATH % tenant_id
mapped_network = self._get_mapped_network_with_subnets(new_net)
data = { data = {
"network": { "network": mapped_network
"id": new_net["id"],
"name": new_net["name"],
}
} }
ret = self.servers.post(resource, data) ret = self.servers.post(resource, data)
if not self.servers.action_success(ret): if not self.servers.action_success(ret):
@ -379,35 +406,27 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
LOG.debug(_("QuantumRestProxyV2.update_network() called")) LOG.debug(_("QuantumRestProxyV2.update_network() called"))
# Validate Args self._warn_on_state_status(network['network'])
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))
# update DB session = context.session
orig_net = super(QuantumRestProxyV2, self).get_network(context, net_id) with session.begin(subtransactions=True):
tenant_id = orig_net["tenant_id"] orig_net = super(QuantumRestProxyV2, self).get_network(context,
new_net = super(QuantumRestProxyV2, self).update_network( net_id)
context, net_id, network) 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 # update network on network controller
if new_net["name"] != orig_net["name"]:
try: try:
resource = NETWORKS_PATH % (tenant_id, net_id) self._send_update_network(new_net)
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: except RemoteRestError as e:
LOG.error(_("QuantumRestProxyV2: Unable to update remote " LOG.error(_("QuantumRestProxyV2: Unable to update remote "
"network: %s"), e.message) "network: %s"), e.message)
# reset network to original state # reset network to original state
super(QuantumRestProxyV2, self).update_network( super(QuantumRestProxyV2, self).update_network(context, id,
context, id, orig_net) orig_net)
raise raise
# return updated network # return updated network
@ -430,6 +449,17 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
orig_net = super(QuantumRestProxyV2, self).get_network(context, net_id) orig_net = super(QuantumRestProxyV2, self).get_network(context, net_id)
tenant_id = orig_net["tenant_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 # delete from network ctrl. Remote error on delete is ignored
try: try:
resource = NETWORKS_PATH % (tenant_id, net_id) resource = NETWORKS_PATH % (tenant_id, net_id)
@ -442,6 +472,7 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
except RemoteRestError as e: except RemoteRestError as e:
LOG.error(_("QuantumRestProxyV2: Unable to update remote " LOG.error(_("QuantumRestProxyV2: Unable to update remote "
"network: %s"), e.message) "network: %s"), e.message)
raise
def create_port(self, context, port): def create_port(self, context, port):
"""Create a port, which is a connection point of a device """Create a port, which is a connection point of a device
@ -477,24 +508,28 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
net = super(QuantumRestProxyV2, net = super(QuantumRestProxyV2,
self).get_network(context, new_port["network_id"]) 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 # create on networl ctrl
try: try:
resource = PORT_RESOURCE_PATH % (net["tenant_id"], net["id"]) resource = PORT_RESOURCE_PATH % (net["tenant_id"], net["id"])
mapped_port = self._map_state_and_status(new_port)
data = { data = {
"port": { "port": mapped_port
"id": new_port["id"],
"state": "ACTIVE",
}
} }
ret = self.servers.post(resource, data) ret = self.servers.post(resource, data)
if not self.servers.action_success(ret): if not self.servers.action_success(ret):
raise RemoteRestError(ret[2]) raise RemoteRestError(ret[2])
# connect device to network, if present # 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, self._plug_interface(context,
net["tenant_id"], net["id"], net["tenant_id"], net["id"],
new_port["id"], new_port["id"] + "00") new_port["id"], device_id)
except RemoteRestError as e: except RemoteRestError as e:
LOG.error(_("QuantumRestProxyV2: Unable to create remote port: " LOG.error(_("QuantumRestProxyV2: Unable to create remote port: "
"%s"), e.message) "%s"), e.message)
@ -536,6 +571,8 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
""" """
LOG.debug(_("QuantumRestProxyV2: update_port() called")) LOG.debug(_("QuantumRestProxyV2: update_port() called"))
self._warn_on_state_status(port['port'])
# Validate Args # Validate Args
orig_port = super(QuantumRestProxyV2, self).get_port(context, port_id) orig_port = super(QuantumRestProxyV2, self).get_port(context, port_id)
@ -547,7 +584,8 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
try: try:
resource = PORTS_PATH % (orig_port["tenant_id"], resource = PORTS_PATH % (orig_port["tenant_id"],
orig_port["network_id"], port_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) ret = self.servers.put(resource, data)
if not self.servers.action_success(ret): if not self.servers.action_success(ret):
raise RemoteRestError(ret[2]) raise RemoteRestError(ret[2])
@ -557,10 +595,11 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
self._unplug_interface(context, orig_port["tenant_id"], self._unplug_interface(context, orig_port["tenant_id"],
orig_port["network_id"], orig_port["network_id"],
orig_port["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"], self._plug_interface(context, new_port["tenant_id"],
new_port["network_id"], new_port["network_id"],
new_port["id"], new_port["id"] + "00") new_port["id"], device_id)
except RemoteRestError as e: except RemoteRestError as e:
LOG.error(_("QuantumRestProxyV2: Unable to create remote port: " 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
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. """Delete a port.
:param context: quantum api request context :param context: quantum api request context
:param id: UUID representing the port to delete. :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")) 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 # Delete from DB
port = super(QuantumRestProxyV2, self).get_port(context, port_id) 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"): if port.get("device_id"):
self._unplug_interface(context, port["tenant_id"], self._unplug_interface(context, port["tenant_id"],
port["network_id"], port["id"]) port["network_id"], port["id"])
ret_val = super(QuantumRestProxyV2, self).delete_port(context, ret_val = super(QuantumRestProxyV2, self)._delete_port(context,
port_id) port_id)
return ret_val return ret_val
except RemoteRestError as e: except RemoteRestError as e:
LOG.error(_("QuantumRestProxyV2: Unable to update remote port: " LOG.error(_("QuantumRestProxyV2: Unable to update remote port: "
"%s"), e.message) "%s"), e.message)
raise
def _plug_interface(self, context, tenant_id, net_id, port_id, def _plug_interface(self, context, tenant_id, net_id, port_id,
remote_interface_id): remote_interface_id):
@ -625,22 +674,6 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
port = super(QuantumRestProxyV2, self).get_port(context, port_id) port = super(QuantumRestProxyV2, self).get_port(context, port_id)
mac = port["mac_address"] 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: if mac is not None:
resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id) resource = ATTACHMENT_PATH % (tenant_id, net_id, port_id)
data = {"attachment": data = {"attachment":
@ -676,31 +709,326 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
LOG.error(_("QuantumRestProxyV2: Unable to update remote port: " LOG.error(_("QuantumRestProxyV2: Unable to update remote port: "
"%s"), e.message) "%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): def _send_all_data(self):
"""Pushes all data to network ctrl (networks/ports, ports/attachments) """Pushes all data to network ctrl (networks/ports, ports/attachments)
to give the controller an option to re-sync it's persistent store to give the controller an option to re-sync it's persistent store
with quantum's current view of that data. with quantum's current view of that data.
""" """
admin_context = qcontext.get_admin_context() admin_context = qcontext.get_admin_context()
networks = {} networks = []
ports = {} routers = []
all_networks = super(QuantumRestProxyV2, all_networks = super(QuantumRestProxyV2,
self).get_networks(admin_context) or [] self).get_networks(admin_context) or []
for net in all_networks: for net in all_networks:
networks[net.get('id')] = { mapped_network = self._get_mapped_network_with_subnets(net)
'id': net.get('id'), net_fl_ips = self._get_network_with_floatingips(mapped_network)
'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
ports = [] ports = []
net_filter = {'network_id': [net.get('id')]} net_filter = {'network_id': [net.get('id')]}
@ -708,28 +1036,166 @@ class QuantumRestProxyV2(db_base_plugin_v2.QuantumDbPluginV2):
self).get_ports(admin_context, self).get_ports(admin_context,
filters=net_filter) or [] filters=net_filter) or []
for port in net_ports: for port in net_ports:
port_details = { mapped_port = self._map_state_and_status(port)
'id': port.get('id'), mapped_port['attachment'] = {
'attachment': { 'id': port.get('device_id'),
'id': port.get('id') + '00',
'mac': port.get('mac_address'), 'mac': port.get('mac_address'),
},
'state': port.get('status'),
'op-status': port.get('admin_state_up'),
'mac': None
} }
ports.append(port_details) ports.append(mapped_port)
networks[net.get('id')]['ports'] = ports 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: try:
resource = '/topology' resource = '/topology'
data = { data = {
'networks': networks, 'networks': networks,
'routers': routers,
} }
ret = self.servers.put(resource, data) ret = self.servers.put(resource, data)
if not self.servers.action_success(ret): if not self.servers.action_success(ret):
raise RemoteRestError(ret[2]) raise RemoteRestError(ret[2])
return ret return ret
except RemoteRestError as e: except RemoteRestError as e:
LOG.error(_('QuantumRestProxy: Unable to update remote network: ' LOG.error(_('QuantumRestProxy: Unable to update remote '
'%s'), e.message) 'topology: %s'), e.message)
raise 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

View 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

View File

@ -23,16 +23,22 @@
# if vcsversion exists, use it. Else, use LOCALBRANCH:LOCALREVISION # if vcsversion exists, use it. Else, use LOCALBRANCH:LOCALREVISION
try: try:
from bigswitch.vcsversion import version_info from quantum.plugins.bigswitch.vcsversion import version_info
except ImportError: except ImportError:
version_info = {'branch_nick': u'LOCALBRANCH', version_info = {'branch_nick': u'LOCALBRANCH',
'revision_id': u'LOCALREVISION', 'revision_id': u'LOCALREVISION',
'revno': 0} '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 YEAR, COUNT, REVISION = QUANTUMRESTPROXY_VERSION
FINAL = False # This becomes true at Release Candidate time
def canonical_version_string(): def canonical_version_string():

View 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)