Merge "L3 API support for nicira plugin"
This commit is contained in:
commit
75ca98567d
@ -1,5 +1,10 @@
|
||||
[DEFAULT]
|
||||
|
||||
# The following flag will cause a host route to the metadata server
|
||||
# to be injected into instances. The metadata server will be reached
|
||||
# via the dhcp server.
|
||||
metadata_dhcp_host_route = False
|
||||
|
||||
[DATABASE]
|
||||
# This line MUST be changed to actually run the plugin.
|
||||
# Example:
|
||||
@ -28,9 +33,6 @@ reconnect_interval = 2
|
||||
# max_lp_per_bridged_ls = 64
|
||||
# Maximum number of ports for each overlay (stt, gre) logical switch
|
||||
# max_lp_per_overlay_ls = 256
|
||||
# Time from when a connection pool is switched to another controller
|
||||
# during failure.
|
||||
# failover_time = 5
|
||||
# Number of connects to each controller node.
|
||||
# concurrent_connections = 3
|
||||
# Name of the default cluster where requests should be sent if a nova zone id
|
||||
@ -53,6 +55,11 @@ reconnect_interval = 2
|
||||
# console "admin" section.
|
||||
# nvp_cluster_uuid = 615be8e4-82e9-4fd2-b4b3-fd141e51a5a7 # (Optional)
|
||||
|
||||
# UUID of the default layer 3 gateway service to use for this cluster
|
||||
# This is optional, but should be filled if planning to use logical routers
|
||||
# with external gateways
|
||||
# default_l3_gw_service_uuid =
|
||||
|
||||
# This parameter describes a connection to a single NVP controller. Format:
|
||||
# <ip>:<port>:<user>:<pw>:<req_timeout>:<http_timeout>:<retries>:<redirects>
|
||||
# <ip> is the ip address of the controller
|
||||
|
@ -0,0 +1,60 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
# Copyright 2013 OpenStack LLC
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
"""nvp_portmap
|
||||
|
||||
Revision ID: 38335592a0dc
|
||||
Revises: 49332180ca96
|
||||
Create Date: 2013-01-15 06:04:56.328991
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '38335592a0dc'
|
||||
down_revision = '49332180ca96'
|
||||
|
||||
# Change to ['*'] if this migration applies to all plugins
|
||||
|
||||
migration_for_plugins = [
|
||||
'quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2'
|
||||
]
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
from quantum.db import migration
|
||||
|
||||
|
||||
def upgrade(active_plugin=None, options=None):
|
||||
if not migration.should_run(active_plugin, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.create_table(
|
||||
'quantum_nvp_port_mapping',
|
||||
sa.Column('quantum_id', sa.String(length=36), nullable=False),
|
||||
sa.Column('nvp_id', sa.String(length=36), nullable=True),
|
||||
sa.ForeignKeyConstraint(['quantum_id'], ['ports.id'],
|
||||
ondelete='CASCADE'),
|
||||
sa.PrimaryKeyConstraint('quantum_id'))
|
||||
|
||||
|
||||
def downgrade(active_plugin=None, options=None):
|
||||
if not migration.should_run(active_plugin, migration_for_plugins):
|
||||
return
|
||||
|
||||
op.drop_table('quantum_nvp_port_mapping')
|
File diff suppressed because it is too large
Load Diff
@ -17,6 +17,10 @@
|
||||
from quantum.openstack.common import cfg
|
||||
|
||||
|
||||
core_opts = [
|
||||
cfg.BoolOpt('metadata_dhcp_host_route', default=False),
|
||||
]
|
||||
|
||||
nvp_opts = [
|
||||
cfg.IntOpt('max_lp_per_bridged_ls', default=64,
|
||||
help=_("Maximum number of ports of a logical switch on a "
|
||||
@ -51,8 +55,15 @@ cluster_opts = [
|
||||
"controller. A different connection for each "
|
||||
"controller in the cluster can be specified; "
|
||||
"there must be at least one connection per "
|
||||
"cluster."))
|
||||
"cluster.")),
|
||||
cfg.StrOpt('default_l3_gw_service_uuid',
|
||||
help=_("Unique identifier of the NVP L3 Gateway service "
|
||||
"which will be used for implementing routers and "
|
||||
"floating IPs"))
|
||||
]
|
||||
|
||||
# Register the configuration options
|
||||
cfg.CONF.register_opts(core_opts)
|
||||
cfg.CONF.register_opts(nvp_opts, "NVP")
|
||||
|
||||
|
||||
|
@ -40,3 +40,9 @@ class NvpNoMorePortsException(NvpPluginException):
|
||||
|
||||
class NvpOutOfSyncException(NvpPluginException):
|
||||
message = _("Quantum state has diverged from the networking backend!")
|
||||
|
||||
|
||||
class NvpNatRuleMismatch(NvpPluginException):
|
||||
message = _("While retrieving NAT rules, %(actual_rules)s were found "
|
||||
"whereas rules in the (%(min_rules)s,%(max_rules)s) interval "
|
||||
"were expected")
|
||||
|
@ -54,3 +54,20 @@ def add_network_binding(session, network_id, binding_type, tz_uuid, vlan_id):
|
||||
tz_uuid, vlan_id)
|
||||
session.add(binding)
|
||||
return binding
|
||||
|
||||
|
||||
def add_quantum_nvp_port_mapping(session, quantum_id, nvp_id):
|
||||
with session.begin(subtransactions=True):
|
||||
mapping = nicira_models.QuantumNvpPortMapping(quantum_id, nvp_id)
|
||||
session.add(mapping)
|
||||
return mapping
|
||||
|
||||
|
||||
def get_nvp_port_id(session, quantum_id):
|
||||
try:
|
||||
mapping = (session.query(nicira_models.QuantumNvpPortMapping).
|
||||
filter_by(quantum_id=quantum_id).
|
||||
one())
|
||||
return mapping['nvp_id']
|
||||
except exc.NoResultFound:
|
||||
return
|
||||
|
@ -50,3 +50,17 @@ class NvpNetworkBinding(model_base.BASEV2):
|
||||
self.binding_type,
|
||||
self.tz_uuid,
|
||||
self.vlan_id)
|
||||
|
||||
|
||||
class QuantumNvpPortMapping(model_base.BASEV2):
|
||||
"""Represents the mapping between quantum and nvp port uuids."""
|
||||
|
||||
__tablename__ = 'quantum_nvp_port_mapping'
|
||||
quantum_id = Column(String(36),
|
||||
ForeignKey('ports.id', ondelete="CASCADE"),
|
||||
primary_key=True)
|
||||
nvp_id = Column(String(36))
|
||||
|
||||
def __init__(self, quantum_id, nvp_id):
|
||||
self.quantum_id = quantum_id
|
||||
self.nvp_id = nvp_id
|
||||
|
@ -14,6 +14,16 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
#
|
||||
|
||||
import re
|
||||
|
||||
from quantum.api.v2 import attributes
|
||||
from quantum.openstack.common import log as logging
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NVPCluster(object):
|
||||
@ -45,8 +55,9 @@ class NVPCluster(object):
|
||||
return ''.join(ss)
|
||||
|
||||
def add_controller(self, ip, port, user, password, request_timeout,
|
||||
http_timeout, retries, redirects,
|
||||
default_tz_uuid, uuid=None, zone=None):
|
||||
http_timeout, retries, redirects, default_tz_uuid,
|
||||
uuid=None, zone=None,
|
||||
default_l3_gw_service_uuid=None):
|
||||
"""Add a new set of controller parameters.
|
||||
|
||||
:param ip: IP address of controller.
|
||||
@ -59,14 +70,33 @@ class NVPCluster(object):
|
||||
:param redirects: maximum number of server redirect responses to
|
||||
follow.
|
||||
:param default_tz_uuid: default transport zone uuid.
|
||||
:param default_next_hop: default next hop for routers in this cluster.
|
||||
:param uuid: UUID of this cluster (used in MDI configs).
|
||||
:param zone: Zone of this cluster (used in MDI configs).
|
||||
"""
|
||||
|
||||
keys = [
|
||||
'ip', 'user', 'password', 'default_tz_uuid', 'uuid', 'zone']
|
||||
keys = ['ip', 'user', 'password', 'default_tz_uuid',
|
||||
'default_l3_gw_service_uuid', 'uuid', 'zone']
|
||||
controller_dict = dict([(k, locals()[k]) for k in keys])
|
||||
|
||||
default_tz_uuid = controller_dict.get('default_tz_uuid')
|
||||
if not re.match(attributes.UUID_PATTERN, default_tz_uuid):
|
||||
LOG.warning(_("default_tz_uuid:%(default_tz_uuid)s is not a "
|
||||
"valid UUID in the cluster %(cluster_name)s. "
|
||||
"Network operations might not work "
|
||||
"properly in this cluster"),
|
||||
{'default_tz_uuid': default_tz_uuid,
|
||||
'cluster_name': self.name})
|
||||
# default_l3_gw_service_uuid is an optional parameter
|
||||
# validate only if specified
|
||||
l3_gw_service_uuid = controller_dict.get('default_l3_gw_service_uuid')
|
||||
if (l3_gw_service_uuid and
|
||||
not re.match(attributes.UUID_PATTERN, l3_gw_service_uuid)):
|
||||
LOG.warning(_("default_l3_gw_service_uuid:%(l3_gw_service_uuid)s "
|
||||
"is not a valid UUID in the cluster "
|
||||
"%(cluster_name)s. Logical router operations "
|
||||
"might not work properly in this cluster"),
|
||||
{'l3_gw_service_uuid': l3_gw_service_uuid,
|
||||
'cluster_name': self.name})
|
||||
int_keys = [
|
||||
'port', 'request_timeout', 'http_timeout', 'retries', 'redirects']
|
||||
for k in int_keys:
|
||||
@ -121,6 +151,10 @@ class NVPCluster(object):
|
||||
def default_tz_uuid(self):
|
||||
return self.controllers[0]['default_tz_uuid']
|
||||
|
||||
@property
|
||||
def default_l3_gw_service_uuid(self):
|
||||
return self.controllers[0]['default_l3_gw_service_uuid']
|
||||
|
||||
@property
|
||||
def zone(self):
|
||||
return self.controllers[0]['zone']
|
||||
|
@ -22,7 +22,6 @@
|
||||
|
||||
from copy import copy
|
||||
import hashlib
|
||||
import itertools
|
||||
import json
|
||||
import logging
|
||||
|
||||
@ -30,6 +29,8 @@ import logging
|
||||
# no quantum-specific logic in it
|
||||
from quantum.common import constants
|
||||
from quantum.common import exceptions as exception
|
||||
from quantum.plugins.nicira.nicira_nvp_plugin.common import (
|
||||
exceptions as nvp_exc)
|
||||
from quantum.plugins.nicira.nicira_nvp_plugin import NvpApiClient
|
||||
|
||||
|
||||
@ -42,7 +43,21 @@ DEF_TRANSPORT_TYPE = "stt"
|
||||
URI_PREFIX = "/ws.v1"
|
||||
# Resources exposed by NVP API
|
||||
LSWITCH_RESOURCE = "lswitch"
|
||||
LPORT_RESOURCE = "lport"
|
||||
LSWITCHPORT_RESOURCE = "lport-%s" % LSWITCH_RESOURCE
|
||||
LROUTER_RESOURCE = "lrouter"
|
||||
LROUTERPORT_RESOURCE = "lport-%s" % LROUTER_RESOURCE
|
||||
LROUTERNAT_RESOURCE = "nat-lrouter"
|
||||
|
||||
# Constants for NAT rules
|
||||
MATCH_KEYS = ["destination_ip_addresses", "destination_port_max",
|
||||
"destination_port_min", "source_ip_addresses",
|
||||
"source_port_max", "source_port_min", "protocol"]
|
||||
|
||||
SNAT_KEYS = ["to_src_port_min", "to_src_port_max", "to_src_ip_min",
|
||||
"to_src_ip_max"]
|
||||
|
||||
DNAT_KEYS = ["to_dst_port", "to_dst_ip_min", "to_dst_ip_max"]
|
||||
|
||||
|
||||
LOCAL_LOGGING = False
|
||||
if LOCAL_LOGGING:
|
||||
@ -71,29 +86,27 @@ def _build_uri_path(resource,
|
||||
resource_id=None,
|
||||
parent_resource_id=None,
|
||||
fields=None,
|
||||
relations=None, filters=None):
|
||||
# TODO(salvatore-orlando): This is ugly. do something more clever
|
||||
# and aovid the if statement
|
||||
if resource == LPORT_RESOURCE:
|
||||
res_path = ("%s/%s/%s" % (LSWITCH_RESOURCE,
|
||||
parent_resource_id,
|
||||
resource) +
|
||||
(resource_id and "/%s" % resource_id or ''))
|
||||
else:
|
||||
res_path = resource + (resource_id and
|
||||
"/%s" % resource_id or '')
|
||||
|
||||
relations=None, filters=None, is_attachment=False):
|
||||
resources = resource.split('-')
|
||||
res_path = resources[0] + (resource_id and "/%s" % resource_id or '')
|
||||
if len(resources) > 1:
|
||||
# There is also a parent resource to account for in the uri
|
||||
res_path = "%s/%s/%s" % (resources[1],
|
||||
parent_resource_id,
|
||||
res_path)
|
||||
if is_attachment:
|
||||
res_path = "%s/attachment" % res_path
|
||||
params = []
|
||||
params.append(fields and "fields=%s" % fields)
|
||||
params.append(relations and "relations=%s" % relations)
|
||||
if filters:
|
||||
params.extend(['%s=%s' % (k, v) for (k, v) in filters.iteritems()])
|
||||
uri_path = "%s/%s" % (URI_PREFIX, res_path)
|
||||
query_string = reduce(lambda x, y: "%s&%s" % (x, y),
|
||||
itertools.ifilter(lambda x: x is not None, params),
|
||||
"")
|
||||
if query_string:
|
||||
uri_path += "?%s" % query_string
|
||||
non_empty_params = [x for x in params if x is not None]
|
||||
if len(non_empty_params):
|
||||
query_string = '&'.join(non_empty_params)
|
||||
if query_string:
|
||||
uri_path += "?%s" % query_string
|
||||
return uri_path
|
||||
|
||||
|
||||
@ -279,6 +292,110 @@ def update_lswitch(cluster, lswitch_id, display_name,
|
||||
return obj
|
||||
|
||||
|
||||
def create_lrouter(cluster, tenant_id, display_name, nexthop):
|
||||
""" Create a NVP logical router on the specified cluster.
|
||||
|
||||
:param cluster: The target NVP cluster
|
||||
:param tenant_id: Identifier of the Openstack tenant for which
|
||||
the logical router is being created
|
||||
:param display_name: Descriptive name of this logical router
|
||||
:param nexthop: External gateway IP address for the logical router
|
||||
:raise NvpApiException: if there is a problem while communicating
|
||||
with the NVP controller
|
||||
"""
|
||||
tags = [{"tag": tenant_id, "scope": "os_tid"}]
|
||||
lrouter_obj = {
|
||||
"display_name": display_name,
|
||||
"tags": tags,
|
||||
"routing_config": {
|
||||
"default_route_next_hop": {
|
||||
"gateway_ip_address": nexthop,
|
||||
"type": "RouterNextHop"
|
||||
},
|
||||
"type": "SingleDefaultRouteImplicitRoutingConfig"
|
||||
},
|
||||
"type": "LogicalRouterConfig"
|
||||
}
|
||||
try:
|
||||
return json.loads(do_single_request("POST",
|
||||
_build_uri_path(LROUTER_RESOURCE),
|
||||
json.dumps(lrouter_obj),
|
||||
cluster=cluster))
|
||||
except NvpApiClient.NvpApiException:
|
||||
# just log and re-raise - let the caller handle it
|
||||
LOG.exception(_("An exception occured while communicating with "
|
||||
"the NVP controller for cluster:%s"), cluster.name)
|
||||
raise
|
||||
|
||||
|
||||
def delete_lrouter(cluster, lrouter_id):
|
||||
try:
|
||||
do_single_request("DELETE",
|
||||
_build_uri_path(LROUTER_RESOURCE,
|
||||
resource_id=lrouter_id),
|
||||
cluster=cluster)
|
||||
except NvpApiClient.NvpApiException:
|
||||
# just log and re-raise - let the caller handle it
|
||||
LOG.exception(_("An exception occured while communicating with "
|
||||
"the NVP controller for cluster:%s"), cluster.name)
|
||||
raise
|
||||
|
||||
|
||||
def get_lrouter(cluster, lrouter_id):
|
||||
try:
|
||||
return json.loads(do_single_request("GET",
|
||||
_build_uri_path(LROUTER_RESOURCE,
|
||||
resource_id=lrouter_id,
|
||||
relations='LogicalRouterStatus'),
|
||||
cluster=cluster))
|
||||
except NvpApiClient.NvpApiException:
|
||||
# just log and re-raise - let the caller handle it
|
||||
LOG.exception(_("An exception occured while communicating with "
|
||||
"the NVP controller for cluster:%s"), cluster.name)
|
||||
raise
|
||||
|
||||
|
||||
def get_lrouters(cluster, tenant_id, fields=None, filters=None):
|
||||
actual_filters = {}
|
||||
if filters:
|
||||
actual_filters.update(filters)
|
||||
if tenant_id:
|
||||
actual_filters['tag'] = tenant_id
|
||||
actual_filters['tag_scope'] = 'os_tid'
|
||||
lrouter_fields = "uuid,display_name,fabric_status,tags"
|
||||
return get_all_query_pages(
|
||||
_build_uri_path(LROUTER_RESOURCE,
|
||||
fields=lrouter_fields,
|
||||
relations='LogicalRouterStatus',
|
||||
filters=actual_filters),
|
||||
cluster)
|
||||
|
||||
|
||||
def update_lrouter(cluster, lrouter_id, display_name, nexthop):
|
||||
lrouter_obj = get_lrouter(cluster, lrouter_id)
|
||||
if not display_name and not nexthop:
|
||||
# Nothing to update
|
||||
return lrouter_obj
|
||||
# It seems that this is faster than the doing an if on display_name
|
||||
lrouter_obj["display_name"] = display_name or lrouter_obj["display_name"]
|
||||
if nexthop:
|
||||
nh_element = lrouter_obj["routing_config"].get(
|
||||
"default_route_next_hop")
|
||||
if nh_element:
|
||||
nh_element["gateway_ip_address"] = nexthop
|
||||
try:
|
||||
return json.loads(do_single_request("PUT",
|
||||
_build_uri_path(LROUTER_RESOURCE,
|
||||
resource_id=lrouter_id),
|
||||
json.dumps(lrouter_obj),
|
||||
cluster=cluster))
|
||||
except NvpApiClient.NvpApiException:
|
||||
# just log and re-raise - let the caller handle it
|
||||
LOG.exception(_("An exception occured while communicating with "
|
||||
"the NVP controller for cluster:%s"), cluster.name)
|
||||
raise
|
||||
|
||||
|
||||
def get_all_networks(cluster, tenant_id, networks):
|
||||
"""Append the quantum network uuids we can find in the given cluster to
|
||||
"networks"
|
||||
@ -330,26 +447,46 @@ def delete_networks(cluster, net_id, lswitch_ids):
|
||||
raise exception.QuantumException()
|
||||
|
||||
|
||||
def query_ports(cluster, network, relations=None, fields="*", filters=None):
|
||||
uri = "/ws.v1/lswitch/" + network + "/lport?"
|
||||
if relations:
|
||||
uri += "relations=%s" % relations
|
||||
uri += "&fields=%s" % fields
|
||||
def query_lswitch_lports(cluster, ls_uuid, fields="*",
|
||||
filters=None, relations=None):
|
||||
# Fix filter for attachments
|
||||
if filters and "attachment" in filters:
|
||||
uri += "&attachment_vif_uuid=%s" % filters["attachment"]
|
||||
filters['attachment_vif_uuid'] = filters["attachment"]
|
||||
del filters['attachment']
|
||||
uri = _build_uri_path(LSWITCHPORT_RESOURCE, parent_resource_id=ls_uuid,
|
||||
fields=fields, filters=filters, relations=relations)
|
||||
try:
|
||||
resp_obj = do_single_request("GET", uri, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.error(_("Network not found, Error: %s"), str(e))
|
||||
raise exception.NetworkNotFound(net_id=network)
|
||||
except NvpApiClient.NvpApiException as e:
|
||||
raise exception.QuantumException()
|
||||
except NvpApiClient.ResourceNotFound:
|
||||
LOG.exception(_("Logical switch: %s not found"), ls_uuid)
|
||||
raise
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_("An error occurred while querying logical ports on "
|
||||
"the NVP platform"))
|
||||
raise
|
||||
return json.loads(resp_obj)["results"]
|
||||
|
||||
|
||||
def delete_port(cluster, port):
|
||||
def query_lrouter_lports(cluster, lr_uuid, fields="*",
|
||||
filters=None, relations=None):
|
||||
uri = _build_uri_path(LROUTERPORT_RESOURCE, parent_resource_id=lr_uuid,
|
||||
fields=fields, filters=filters, relations=relations)
|
||||
try:
|
||||
do_single_request("DELETE", port['_href'], cluster=cluster)
|
||||
resp_obj = do_single_request("GET", uri, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound:
|
||||
LOG.exception(_("Logical router: %s not found"), lr_uuid)
|
||||
raise
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_("An error occured while querying logical router "
|
||||
"ports on the NVP platfom"))
|
||||
raise
|
||||
return json.loads(resp_obj)["results"]
|
||||
|
||||
|
||||
def delete_port(cluster, switch, port):
|
||||
uri = "/ws.v1/lswitch/" + switch + "/lport/" + port
|
||||
try:
|
||||
do_single_request("DELETE", uri, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.error(_("Port or Network not found, Error: %s"), str(e))
|
||||
raise exception.PortNotFound(port_id=port['uuid'])
|
||||
@ -357,27 +494,21 @@ def delete_port(cluster, port):
|
||||
raise exception.QuantumException()
|
||||
|
||||
|
||||
def get_port_by_quantum_tag(clusters, lswitch, quantum_tag):
|
||||
"""Return (url, cluster_id) of port or raises ResourceNotFound
|
||||
"""
|
||||
query = ("/ws.v1/lswitch/%s/lport?fields=admin_status_enabled,"
|
||||
"fabric_status_up,uuid&tag=%s&tag_scope=q_port_id"
|
||||
"&relations=LogicalPortStatus" % (lswitch, quantum_tag))
|
||||
|
||||
LOG.debug(_("Looking for port with q_tag '%(quantum_tag)s' "
|
||||
"on: %(lswitch)s"),
|
||||
locals())
|
||||
for c in clusters:
|
||||
try:
|
||||
res_obj = do_single_request('GET', query, cluster=c)
|
||||
except Exception:
|
||||
continue
|
||||
res = json.loads(res_obj)
|
||||
if len(res["results"]) == 1:
|
||||
return (res["results"][0], c)
|
||||
|
||||
LOG.error(_("Port or Network not found"))
|
||||
raise exception.PortNotFound(port_id=quantum_tag, net_id=lswitch)
|
||||
def get_logical_port_status(cluster, switch, port):
|
||||
query = ("/ws.v1/lswitch/" + switch + "/lport/"
|
||||
+ port + "?relations=LogicalPortStatus")
|
||||
try:
|
||||
res_obj = do_single_request('GET', query, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.error(_("Port or Network not found, Error: %s"), str(e))
|
||||
raise exception.PortNotFound(port_id=port, net_id=switch)
|
||||
except NvpApiClient.NvpApiException as e:
|
||||
raise exception.QuantumException()
|
||||
res = json.loads(res_obj)
|
||||
# copy over admin_status_enabled
|
||||
res["_relations"]["LogicalPortStatus"]["admin_status_enabled"] = (
|
||||
res["admin_status_enabled"])
|
||||
return res["_relations"]["LogicalPortStatus"]
|
||||
|
||||
|
||||
def get_port_by_display_name(clusters, lswitch, display_name):
|
||||
@ -483,7 +614,8 @@ def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
|
||||
_configure_extensions(lport_obj, mac_address, fixed_ips,
|
||||
port_security_enabled, security_profiles)
|
||||
|
||||
path = _build_uri_path(LPORT_RESOURCE, parent_resource_id=lswitch_uuid)
|
||||
path = _build_uri_path(LSWITCHPORT_RESOURCE,
|
||||
parent_resource_id=lswitch_uuid)
|
||||
try:
|
||||
resp_obj = do_single_request("POST", path,
|
||||
json.dumps(lport_obj),
|
||||
@ -498,6 +630,150 @@ def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
|
||||
return result
|
||||
|
||||
|
||||
def create_router_lport(cluster, lrouter_uuid, tenant_id, quantum_port_id,
|
||||
display_name, admin_status_enabled, ip_addresses):
|
||||
""" Creates a logical port on the assigned logical router """
|
||||
tags = [dict(scope='os_tid', tag=tenant_id),
|
||||
dict(scope='q_port_id', tag=quantum_port_id)]
|
||||
lport_obj = dict(
|
||||
admin_status_enabled=admin_status_enabled,
|
||||
display_name=display_name,
|
||||
tags=tags,
|
||||
ip_addresses=ip_addresses,
|
||||
type="LogicalRouterPortConfig"
|
||||
)
|
||||
path = _build_uri_path(LROUTERPORT_RESOURCE,
|
||||
parent_resource_id=lrouter_uuid)
|
||||
try:
|
||||
resp_obj = do_single_request("POST", path,
|
||||
json.dumps(lport_obj),
|
||||
cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.error(_("Logical router not found, Error: %s"), str(e))
|
||||
raise
|
||||
|
||||
result = json.loads(resp_obj)
|
||||
LOG.debug(_("Created logical port %(lport_uuid)s on "
|
||||
"logical router %(lrouter_uuid)s"),
|
||||
{'lport_uuid': result['uuid'],
|
||||
'lrouter_uuid': lrouter_uuid})
|
||||
return result
|
||||
|
||||
|
||||
def update_router_lport(cluster, lrouter_uuid, lrouter_port_uuid,
|
||||
tenant_id, quantum_port_id, display_name,
|
||||
admin_status_enabled, ip_addresses):
|
||||
""" Updates a logical port on the assigned logical router """
|
||||
lport_obj = dict(
|
||||
admin_status_enabled=admin_status_enabled,
|
||||
display_name=display_name,
|
||||
tags=[dict(scope='os_tid', tag=tenant_id),
|
||||
dict(scope='q_port_id', tag=quantum_port_id)],
|
||||
ip_addresses=ip_addresses,
|
||||
type="LogicalRouterPortConfig"
|
||||
)
|
||||
# Do not pass null items to NVP
|
||||
for key in lport_obj.keys():
|
||||
if lport_obj[key] is None:
|
||||
del lport_obj[key]
|
||||
path = _build_uri_path(LROUTERPORT_RESOURCE,
|
||||
lrouter_port_uuid,
|
||||
parent_resource_id=lrouter_uuid)
|
||||
try:
|
||||
resp_obj = do_single_request("PUT", path,
|
||||
json.dumps(lport_obj),
|
||||
cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.error(_("Logical router or router port not found, "
|
||||
"Error: %s"), str(e))
|
||||
raise
|
||||
|
||||
result = json.loads(resp_obj)
|
||||
LOG.debug(_("Updated logical port %(lport_uuid)s on "
|
||||
"logical router %(lrouter_uuid)s"),
|
||||
{'lport_uuid': lrouter_port_uuid, 'lrouter_uuid': lrouter_uuid})
|
||||
return result
|
||||
|
||||
|
||||
def delete_router_lport(cluster, lrouter_uuid, lport_uuid):
|
||||
""" Creates a logical port on the assigned logical router """
|
||||
path = _build_uri_path(LROUTERPORT_RESOURCE, lport_uuid, lrouter_uuid)
|
||||
try:
|
||||
do_single_request("DELETE", path, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.error(_("Logical router not found, Error: %s"), str(e))
|
||||
raise
|
||||
LOG.debug(_("Delete logical router port %(lport_uuid)s on "
|
||||
"logical router %(lrouter_uuid)s"),
|
||||
{'lport_uuid': lport_uuid,
|
||||
'lrouter_uuid': lrouter_uuid})
|
||||
|
||||
|
||||
def delete_peer_router_lport(cluster, lr_uuid, ls_uuid, lp_uuid):
|
||||
nvp_port = get_port(cluster, ls_uuid, lp_uuid,
|
||||
relations="LogicalPortAttachment")
|
||||
try:
|
||||
relations = nvp_port.get('_relations')
|
||||
if relations:
|
||||
att_data = relations.get('LogicalPortAttachment')
|
||||
if att_data:
|
||||
lrp_uuid = att_data.get('peer_port_uuid')
|
||||
if lrp_uuid:
|
||||
delete_router_lport(cluster, lr_uuid, lrp_uuid)
|
||||
except (NvpApiClient.NvpApiException, NvpApiClient.ResourceNotFound):
|
||||
LOG.exception(_("Unable to fetch and delete peer logical "
|
||||
"router port for logical switch port:%s"),
|
||||
lp_uuid)
|
||||
raise
|
||||
|
||||
|
||||
def find_router_gw_port(context, cluster, router_id):
|
||||
""" Retrieves the external gateway port for a NVP logical router """
|
||||
|
||||
# Find the uuid of nvp ext gw logical router port
|
||||
# TODO(salvatore-orlando): Consider storing it in Quantum DB
|
||||
results = query_lrouter_lports(
|
||||
cluster, router_id,
|
||||
filters={'attachment_gwsvc_uuid': cluster.default_l3_gw_service_uuid})
|
||||
if len(results):
|
||||
# Return logical router port
|
||||
return results[0]
|
||||
|
||||
|
||||
def plug_router_port_attachment(cluster, router_id, port_id,
|
||||
attachment_uuid, nvp_attachment_type):
|
||||
"""Attach a router port to the given attachment.
|
||||
Current attachment types:
|
||||
- PatchAttachment [-> logical switch port uuid]
|
||||
- L3GatewayAttachment [-> L3GatewayService uuid]
|
||||
"""
|
||||
uri = _build_uri_path(LROUTERPORT_RESOURCE, port_id, router_id,
|
||||
is_attachment=True)
|
||||
attach_obj = {}
|
||||
attach_obj["type"] = nvp_attachment_type
|
||||
if nvp_attachment_type == "PatchAttachment":
|
||||
attach_obj["peer_port_uuid"] = attachment_uuid
|
||||
elif nvp_attachment_type == "L3GatewayAttachment":
|
||||
attach_obj["l3_gateway_service_uuid"] = attachment_uuid
|
||||
else:
|
||||
raise Exception(_("Invalid NVP attachment type '%s'"),
|
||||
nvp_attachment_type)
|
||||
try:
|
||||
resp_obj = do_single_request(
|
||||
"PUT", uri, json.dumps(attach_obj), cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
LOG.exception(_("Router Port not found, Error: %s"), str(e))
|
||||
raise
|
||||
except NvpApiClient.Conflict as e:
|
||||
LOG.exception(_("Conflict while setting router port attachment"))
|
||||
raise
|
||||
except NvpApiClient.NvpApiException as e:
|
||||
LOG.exception(_("Unable to plug attachment into logical router port"))
|
||||
raise
|
||||
result = json.loads(resp_obj)
|
||||
return result
|
||||
|
||||
|
||||
def get_port_status(cluster, lswitch_id, port_id):
|
||||
"""Retrieve the operational status of the port"""
|
||||
try:
|
||||
@ -653,7 +929,7 @@ def create_security_profile(cluster, tenant_id, security_profile):
|
||||
'logical_port_ingress_rules': []}
|
||||
|
||||
update_security_group_rules(cluster, rsp['uuid'], rules)
|
||||
LOG.debug("Created Security Profile: %s" % rsp)
|
||||
LOG.debug(_("Created Security Profile: %s"), rsp)
|
||||
return rsp
|
||||
|
||||
|
||||
@ -674,7 +950,7 @@ def update_security_group_rules(cluster, spid, rules):
|
||||
except NvpApiClient.NvpApiException as e:
|
||||
LOG.error(format_exception("Unknown", e, locals()))
|
||||
raise exception.QuantumException()
|
||||
LOG.debug("Updated Security Profile: %s" % rsp)
|
||||
LOG.debug(_("Updated Security Profile: %s"), rsp)
|
||||
return rsp
|
||||
|
||||
|
||||
@ -686,3 +962,154 @@ def delete_security_profile(cluster, spid):
|
||||
except NvpApiClient.NvpApiException as e:
|
||||
LOG.error(format_exception("Unknown", e, locals()))
|
||||
raise exception.QuantumException()
|
||||
|
||||
|
||||
def _create_nat_match_obj(**kwargs):
|
||||
nat_match_obj = {'ethertype': 'IPv4'}
|
||||
delta = set(kwargs.keys()) - set(MATCH_KEYS)
|
||||
if delta:
|
||||
raise Exception(_("Invalid keys for NAT match: %s"), delta)
|
||||
nat_match_obj.update(kwargs)
|
||||
return nat_match_obj
|
||||
|
||||
|
||||
def _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj):
|
||||
LOG.debug(_("Creating NAT rule: %s"), nat_rule_obj)
|
||||
uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id)
|
||||
try:
|
||||
resp = do_single_request("POST", uri, json.dumps(nat_rule_obj),
|
||||
cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound:
|
||||
LOG.exception(_("NVP Logical Router %s not found"), router_id)
|
||||
raise
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_("An error occurred while creating the NAT rule "
|
||||
"on the NVP platform"))
|
||||
raise
|
||||
rule = json.loads(resp)
|
||||
return rule
|
||||
|
||||
|
||||
def create_lrouter_snat_rule(cluster, router_id,
|
||||
min_src_ip, max_src_ip, **kwargs):
|
||||
|
||||
nat_match_obj = _create_nat_match_obj(**kwargs)
|
||||
nat_rule_obj = {
|
||||
"to_source_ip_address_min": min_src_ip,
|
||||
"to_source_ip_address_max": max_src_ip,
|
||||
"type": "SourceNatRule",
|
||||
"match": nat_match_obj
|
||||
}
|
||||
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
||||
|
||||
|
||||
def create_lrouter_dnat_rule(cluster, router_id, to_min_dst_ip,
|
||||
to_max_dst_ip, to_dst_port=None, **kwargs):
|
||||
|
||||
nat_match_obj = _create_nat_match_obj(**kwargs)
|
||||
nat_rule_obj = {
|
||||
"to_destination_ip_address_min": to_min_dst_ip,
|
||||
"to_destination_ip_address_max": to_max_dst_ip,
|
||||
"type": "DestinationNatRule",
|
||||
"match": nat_match_obj
|
||||
}
|
||||
if to_dst_port:
|
||||
nat_rule_obj['to_destination_port'] = to_dst_port
|
||||
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
||||
|
||||
|
||||
def delete_nat_rules_by_match(cluster, router_id, rule_type,
|
||||
max_num_expected,
|
||||
min_num_expected=0,
|
||||
**kwargs):
|
||||
# remove nat rules
|
||||
nat_rules = query_nat_rules(cluster, router_id)
|
||||
to_delete_ids = []
|
||||
for r in nat_rules:
|
||||
if (r['type'] != rule_type):
|
||||
continue
|
||||
|
||||
for key, value in kwargs.iteritems():
|
||||
if not (key in r['match'] and r['match'][key] == value):
|
||||
break
|
||||
else:
|
||||
to_delete_ids.append(r['uuid'])
|
||||
if not (len(to_delete_ids) in
|
||||
range(min_num_expected, max_num_expected + 1)):
|
||||
raise nvp_exc.NvpNatRuleMismatch(actual_rules=len(to_delete_ids),
|
||||
min_rules=min_num_expected,
|
||||
max_rules=max_num_expected)
|
||||
|
||||
for rule_id in to_delete_ids:
|
||||
delete_router_nat_rule(cluster, router_id, rule_id)
|
||||
|
||||
|
||||
def delete_router_nat_rule(cluster, router_id, rule_id):
|
||||
uri = _build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id)
|
||||
try:
|
||||
do_single_request("DELETE", uri, cluster=cluster)
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_("An error occurred while removing NAT rule "
|
||||
"'%(nat_rule_uuid)s' for logical "
|
||||
"router '%(lrouter_uuid)s'"),
|
||||
{'nat_rule_uuid': rule_id, 'lrouter_uuid': router_id})
|
||||
raise
|
||||
|
||||
|
||||
def get_router_nat_rule(cluster, tenant_id, router_id, rule_id):
|
||||
uri = _build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id)
|
||||
try:
|
||||
resp = do_single_request("GET", uri, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound:
|
||||
LOG.exception(_("NAT rule %s not found"), rule_id)
|
||||
raise
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_("An error occured while retrieving NAT rule '%s'"
|
||||
"from NVP platform"), rule_id)
|
||||
raise
|
||||
res = json.loads(resp)
|
||||
return res
|
||||
|
||||
|
||||
def query_nat_rules(cluster, router_id, fields="*", filters=None):
|
||||
uri = _build_uri_path(LROUTERNAT_RESOURCE, parent_resource_id=router_id,
|
||||
fields=fields, filters=filters)
|
||||
try:
|
||||
resp = do_single_request("GET", uri, cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound:
|
||||
LOG.exception(_("NVP Logical Router '%s' not found"), router_id)
|
||||
raise
|
||||
except NvpApiClient.NvpApiException:
|
||||
LOG.exception(_("An error occured while retrieving NAT rules for "
|
||||
"NVP logical router '%s'"), router_id)
|
||||
raise
|
||||
res = json.loads(resp)
|
||||
return res["results"]
|
||||
|
||||
|
||||
# NOTE(salvatore-orlando): The following FIXME applies in general to
|
||||
# each operation on list attributes.
|
||||
# FIXME(salvatore-orlando): need a lock around the list of IPs on an iface
|
||||
def update_lrouter_port_ips(cluster, lrouter_id, lport_id,
|
||||
ips_to_add, ips_to_remove):
|
||||
uri = _build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id)
|
||||
try:
|
||||
port = json.loads(do_single_request("GET", uri, cluster=cluster))
|
||||
# TODO(salvatore-orlando): Enforce ips_to_add intersection with
|
||||
# ips_to_remove is empty
|
||||
ip_address_set = set(port['ip_addresses'])
|
||||
ip_address_set = ip_address_set - set(ips_to_remove)
|
||||
ip_address_set = ip_address_set | set(ips_to_add)
|
||||
# Set is not JSON serializable - convert to list
|
||||
port['ip_addresses'] = list(ip_address_set)
|
||||
do_single_request("PUT", uri, json.dumps(port), cluster=cluster)
|
||||
except NvpApiClient.ResourceNotFound as e:
|
||||
msg = (_("Router Port %(lport_id)s not found on router "
|
||||
"%(lrouter_id)s") % locals())
|
||||
LOG.exception(msg)
|
||||
raise nvp_exc.NvpPluginException(err_desc=msg)
|
||||
except NvpApiClient.NvpApiException as e:
|
||||
msg = _("An exception occurred while updating IP addresses on a "
|
||||
"router logical port:%s") % str(e)
|
||||
LOG.exception(msg)
|
||||
raise nvp_exc.NvpPluginException(err_desc=msg)
|
||||
|
28
quantum/tests/unit/nicira/etc/fake_get_lrouter.json
Normal file
28
quantum/tests/unit/nicira/etc/fake_get_lrouter.json
Normal file
@ -0,0 +1,28 @@
|
||||
{
|
||||
"display_name": "%(display_name)s",
|
||||
"uuid": "%(uuid)s",
|
||||
"tags": %(tags_json)s,
|
||||
"routing_config": {
|
||||
"type": "SingleDefaultRouteImplicitRoutingConfig",
|
||||
"_schema": "/ws.v1/schema/SingleDefaultRouteImplicitRoutingConfig",
|
||||
"default_route_next_hop": {
|
||||
"type": "RouterNextHop",
|
||||
"_schema": "/ws.v1/schema/RouterNextHop",
|
||||
"gateway_ip_address": "%(default_next_hop)s"
|
||||
}
|
||||
},
|
||||
"_schema": "/ws.v1/schema/LogicalRouterConfig",
|
||||
"_relations": {
|
||||
"LogicalRouterStatus": {
|
||||
"_href": "/ws.v1/lrouter/%(uuid)s/status",
|
||||
"lport_admin_up_count": %(lport_count)d,
|
||||
"_schema": "/ws.v1/schema/LogicalRouterStatus",
|
||||
"lport_count": %(lport_count)d,
|
||||
"fabric_status": true,
|
||||
"type": "LogicalRouterStatus",
|
||||
"lport_link_up_count": %(lport_count)d
|
||||
}
|
||||
},
|
||||
"type": "LogicalRouterConfig",
|
||||
"_href": "/ws.v1/lrouter/%(uuid)s"
|
||||
}
|
11
quantum/tests/unit/nicira/etc/fake_get_lrouter_lport.json
Normal file
11
quantum/tests/unit/nicira/etc/fake_get_lrouter_lport.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"display_name": "%(display_name)s",
|
||||
"_href": "/ws.v1/lrouter/%(lr_uuid)s/lport/%(uuid)s",
|
||||
"tags":
|
||||
[{"scope": "q_port_id", "tag": "%(quantum_port_id)s"},
|
||||
{"scope": "os_tid", "tag": "%(tenant_id)s"}],
|
||||
"ip_addresses": %(ip_addresses_json)s,
|
||||
"_schema": "/ws.v1/schema/LogicalRouterPortConfig",
|
||||
"type": "LogicalRouterPortConfig",
|
||||
"uuid": "%(uuid)s"
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"LogicalPortAttachment":
|
||||
{
|
||||
%(peer_port_href_field)s
|
||||
%(peer_port_uuid_field)s
|
||||
"type": "%(type)s",
|
||||
"schema": "/ws.v1/schema/%(type)s"
|
||||
}
|
||||
}
|
6
quantum/tests/unit/nicira/etc/fake_get_lrouter_nat.json
Normal file
6
quantum/tests/unit/nicira/etc/fake_get_lrouter_nat.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"_href": "/ws.v1/lrouter/%(lr_uuid)s/nat/%(uuid)s",
|
||||
"type": "%(type)s",
|
||||
"match": %(match_json)s,
|
||||
"uuid": "%(uuid)s"
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"LogicalPortAttachment":
|
||||
{
|
||||
%(peer_port_href_field)s
|
||||
%(peer_port_uuid_field)s
|
||||
%(vif_uuid_field)s
|
||||
"type": "%(type)s",
|
||||
"schema": "/ws.v1/schema/%(type)s"
|
||||
}
|
||||
}
|
22
quantum/tests/unit/nicira/etc/fake_post_lrouter.json
Normal file
22
quantum/tests/unit/nicira/etc/fake_post_lrouter.json
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"display_name": "%(display_name)s",
|
||||
"uuid": "%(uuid)s",
|
||||
"tags": [
|
||||
{
|
||||
"scope": "os_tid",
|
||||
"tag": "%(tenant_id)s"
|
||||
}
|
||||
],
|
||||
"routing_config": {
|
||||
"type": "SingleDefaultRouteImplicitRoutingConfig",
|
||||
"_schema": "/ws.v1/schema/SingleDefaultRouteImplicitRoutingConfig",
|
||||
"default_route_next_hop": {
|
||||
"type": "RouterNextHop",
|
||||
"_schema": "/ws.v1/schema/RouterNextHop",
|
||||
"gateway_ip_address": "%(default_next_hop)s"
|
||||
}
|
||||
},
|
||||
"_schema": "/ws.v1/schema/LogicalRouterConfig",
|
||||
"type": "LogicalRouterConfig",
|
||||
"_href": "/ws.v1/lrouter/%(uuid)s"
|
||||
}
|
10
quantum/tests/unit/nicira/etc/fake_post_lrouter_lport.json
Normal file
10
quantum/tests/unit/nicira/etc/fake_post_lrouter_lport.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"display_name": "%(display_name)s",
|
||||
"_href": "/ws.v1/lrouter/%(lr_uuid)s/lport/%(uuid)s",
|
||||
"_schema": "/ws.v1/schema/LogicalRouterPortConfig",
|
||||
"mac_address": "00:00:00:00:00:00",
|
||||
"admin_status_enabled": true,
|
||||
"ip_addresses": %(ip_addresses_json)s,
|
||||
"type": "LogicalRouterPortConfig",
|
||||
"uuid": "%(uuid)s"
|
||||
}
|
6
quantum/tests/unit/nicira/etc/fake_post_lrouter_nat.json
Normal file
6
quantum/tests/unit/nicira/etc/fake_post_lrouter_nat.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"_href": "/ws.v1/lrouter/%(lr_uuid)s/nat/%(uuid)s",
|
||||
"type": "%(type)s",
|
||||
"match": %(match_json)s,
|
||||
"uuid": "%(uuid)s"
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"LogicalPortAttachment":
|
||||
{
|
||||
%(peer_port_href_field)s
|
||||
%(peer_port_uuid_field)s
|
||||
"_href": "/ws.v1/lrouter/%(lr_uuid)s/lport/%(lp_uuid)s/attachment",
|
||||
"type": "%(type)s",
|
||||
"schema": "/ws.v1/schema/%(type)s"
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
{
|
||||
"LogicalPortAttachment":
|
||||
{
|
||||
%(peer_port_href_field)s
|
||||
%(peer_port_uuid_field)s
|
||||
%(vif_uuid_field)s
|
||||
"_href": "/ws.v1/lswitch/%(ls_uuid)s/lport/%(lp_uuid)s/attachment",
|
||||
"type": "%(type)s",
|
||||
"schema": "/ws.v1/schema/%(type)s"
|
||||
}
|
||||
}
|
@ -4,4 +4,5 @@
|
||||
default_tz_uuid = fake_tz_uuid
|
||||
nova_zone_id = whatever
|
||||
nvp_cluster_uuid = fake_cluster_uuid
|
||||
nvp_controller_connection=fake:443:admin:admin:30:10:2:2
|
||||
nvp_controller_connection=fake:443:admin:admin:30:10:2:2
|
||||
default_l3_gw_uuid = whatever
|
||||
|
@ -15,39 +15,82 @@
|
||||
# under the License.
|
||||
|
||||
import json
|
||||
import logging
|
||||
import urlparse
|
||||
|
||||
from quantum.openstack.common import log as logging
|
||||
from quantum.openstack.common import uuidutils
|
||||
|
||||
|
||||
LOG = logging.getLogger("fake_nvpapiclient")
|
||||
LOG.setLevel(logging.DEBUG)
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FakeClient:
|
||||
|
||||
LSWITCH_RESOURCE = 'lswitch'
|
||||
LPORT_RESOURCE = 'lport'
|
||||
LROUTER_RESOURCE = 'lrouter'
|
||||
NAT_RESOURCE = 'nat'
|
||||
SECPROF_RESOURCE = 'securityprofile'
|
||||
LSWITCH_STATUS = 'lswitchstatus'
|
||||
LROUTER_STATUS = 'lrouterstatus'
|
||||
LSWITCH_LPORT_RESOURCE = 'lswitch_lport'
|
||||
LROUTER_LPORT_RESOURCE = 'lrouter_lport'
|
||||
LROUTER_NAT_RESOURCE = 'lrouter_nat'
|
||||
LSWITCH_LPORT_STATUS = 'lswitch_lportstatus'
|
||||
LSWITCH_LPORT_ATT = 'lswitch_lportattachment'
|
||||
LROUTER_LPORT_STATUS = 'lrouter_lportstatus'
|
||||
LROUTER_LPORT_ATT = 'lrouter_lportattachment'
|
||||
|
||||
RESOURCES = [LSWITCH_RESOURCE, LROUTER_RESOURCE,
|
||||
LPORT_RESOURCE, NAT_RESOURCE, SECPROF_RESOURCE]
|
||||
|
||||
FAKE_GET_RESPONSES = {
|
||||
"lswitch": "fake_get_lswitch.json",
|
||||
"lport": "fake_get_lport.json",
|
||||
"lportstatus": "fake_get_lport_status.json"
|
||||
LSWITCH_RESOURCE: "fake_get_lswitch.json",
|
||||
LSWITCH_LPORT_RESOURCE: "fake_get_lswitch_lport.json",
|
||||
LSWITCH_LPORT_STATUS: "fake_get_lswitch_lport_status.json",
|
||||
LSWITCH_LPORT_ATT: "fake_get_lswitch_lport_att.json",
|
||||
LROUTER_RESOURCE: "fake_get_lrouter.json",
|
||||
LROUTER_LPORT_RESOURCE: "fake_get_lrouter_lport.json",
|
||||
LROUTER_LPORT_STATUS: "fake_get_lrouter_lport_status.json",
|
||||
LROUTER_LPORT_ATT: "fake_get_lrouter_lport_att.json",
|
||||
LROUTER_STATUS: "fake_get_lrouter_status.json",
|
||||
LROUTER_NAT_RESOURCE: "fake_get_lrouter_nat.json"
|
||||
}
|
||||
|
||||
FAKE_POST_RESPONSES = {
|
||||
"lswitch": "fake_post_lswitch.json",
|
||||
"lport": "fake_post_lport.json",
|
||||
"securityprofile": "fake_post_security_profile.json"
|
||||
LSWITCH_RESOURCE: "fake_post_lswitch.json",
|
||||
LROUTER_RESOURCE: "fake_post_lrouter.json",
|
||||
LSWITCH_LPORT_RESOURCE: "fake_post_lswitch_lport.json",
|
||||
LROUTER_LPORT_RESOURCE: "fake_post_lrouter_lport.json",
|
||||
LROUTER_NAT_RESOURCE: "fake_post_lrouter_nat.json",
|
||||
SECPROF_RESOURCE: "fake_post_security_profile.json"
|
||||
}
|
||||
|
||||
FAKE_PUT_RESPONSES = {
|
||||
"lswitch": "fake_post_lswitch.json",
|
||||
"lport": "fake_post_lport.json",
|
||||
"securityprofile": "fake_post_security_profile.json"
|
||||
LSWITCH_RESOURCE: "fake_post_lswitch.json",
|
||||
LROUTER_RESOURCE: "fake_post_lrouter.json",
|
||||
LSWITCH_LPORT_RESOURCE: "fake_post_lswitch_lport.json",
|
||||
LROUTER_LPORT_RESOURCE: "fake_post_lrouter_lport.json",
|
||||
LROUTER_NAT_RESOURCE: "fake_post_lrouter_nat.json",
|
||||
LSWITCH_LPORT_ATT: "fake_put_lswitch_lport_att.json",
|
||||
LROUTER_LPORT_ATT: "fake_put_lrouter_lport_att.json",
|
||||
SECPROF_RESOURCE: "fake_post_security_profile.json"
|
||||
}
|
||||
|
||||
MANAGED_RELATIONS = {
|
||||
LSWITCH_RESOURCE: [],
|
||||
LROUTER_RESOURCE: [],
|
||||
LSWITCH_LPORT_RESOURCE: ['LogicalPortAttachment'],
|
||||
LROUTER_LPORT_RESOURCE: ['LogicalPortAttachment'],
|
||||
}
|
||||
|
||||
_fake_lswitch_dict = {}
|
||||
_fake_lport_dict = {}
|
||||
_fake_lportstatus_dict = {}
|
||||
_fake_lrouter_dict = {}
|
||||
_fake_lswitch_lport_dict = {}
|
||||
_fake_lrouter_lport_dict = {}
|
||||
_fake_lrouter_nat_dict = {}
|
||||
_fake_lswitch_lportstatus_dict = {}
|
||||
_fake_lrouter_lportstatus_dict = {}
|
||||
_fake_securityprofile_dict = {}
|
||||
|
||||
def __init__(self, fake_files_path):
|
||||
@ -83,9 +126,22 @@ class FakeClient:
|
||||
fake_lswitch['lport_count'] = 0
|
||||
return fake_lswitch
|
||||
|
||||
def _add_lport(self, body, ls_uuid):
|
||||
def _add_lrouter(self, body):
|
||||
fake_lrouter = json.loads(body)
|
||||
fake_lrouter['uuid'] = uuidutils.generate_uuid()
|
||||
self._fake_lrouter_dict[fake_lrouter['uuid']] = fake_lrouter
|
||||
fake_lrouter['tenant_id'] = self._get_tag(fake_lrouter, 'os_tid')
|
||||
fake_lrouter['lport_count'] = 0
|
||||
default_nexthop = fake_lrouter['routing_config'].get(
|
||||
'default_route_next_hop')
|
||||
fake_lrouter['default_next_hop'] = default_nexthop.get(
|
||||
'gateway_ip_address', '0.0.0.0')
|
||||
return fake_lrouter
|
||||
|
||||
def _add_lswitch_lport(self, body, ls_uuid):
|
||||
fake_lport = json.loads(body)
|
||||
fake_lport['uuid'] = uuidutils.generate_uuid()
|
||||
new_uuid = uuidutils.generate_uuid()
|
||||
fake_lport['uuid'] = new_uuid
|
||||
# put the tenant_id and the ls_uuid in the main dict
|
||||
# for simplyfying templating
|
||||
fake_lport['ls_uuid'] = ls_uuid
|
||||
@ -93,7 +149,7 @@ class FakeClient:
|
||||
fake_lport['quantum_port_id'] = self._get_tag(fake_lport,
|
||||
'q_port_id')
|
||||
fake_lport['quantum_device_id'] = self._get_tag(fake_lport, 'vm_id')
|
||||
self._fake_lport_dict[fake_lport['uuid']] = fake_lport
|
||||
self._fake_lswitch_lport_dict[fake_lport['uuid']] = fake_lport
|
||||
|
||||
fake_lswitch = self._fake_lswitch_dict[ls_uuid]
|
||||
fake_lswitch['lport_count'] += 1
|
||||
@ -102,7 +158,31 @@ class FakeClient:
|
||||
fake_lport_status['ls_uuid'] = fake_lswitch['uuid']
|
||||
fake_lport_status['ls_name'] = fake_lswitch['display_name']
|
||||
fake_lport_status['ls_zone_uuid'] = fake_lswitch['zone_uuid']
|
||||
self._fake_lportstatus_dict[fake_lport['uuid']] = fake_lport_status
|
||||
self._fake_lswitch_lportstatus_dict[new_uuid] = fake_lport_status
|
||||
return fake_lport
|
||||
|
||||
def _add_lrouter_lport(self, body, lr_uuid):
|
||||
fake_lport = json.loads(body)
|
||||
new_uuid = uuidutils.generate_uuid()
|
||||
fake_lport['uuid'] = new_uuid
|
||||
# put the tenant_id and the ls_uuid in the main dict
|
||||
# for simplyfying templating
|
||||
fake_lport['lr_uuid'] = lr_uuid
|
||||
fake_lport['tenant_id'] = self._get_tag(fake_lport, 'os_tid')
|
||||
fake_lport['quantum_port_id'] = self._get_tag(fake_lport,
|
||||
'q_port_id')
|
||||
# replace ip_address with its json dump
|
||||
if 'ip_addresses' in fake_lport:
|
||||
ip_addresses_json = json.dumps(fake_lport['ip_addresses'])
|
||||
fake_lport['ip_addresses_json'] = ip_addresses_json
|
||||
self._fake_lrouter_lport_dict[fake_lport['uuid']] = fake_lport
|
||||
fake_lrouter = self._fake_lrouter_dict[lr_uuid]
|
||||
fake_lrouter['lport_count'] += 1
|
||||
fake_lport_status = fake_lport.copy()
|
||||
fake_lport_status['lr_tenant_id'] = fake_lrouter['tenant_id']
|
||||
fake_lport_status['lr_uuid'] = fake_lrouter['uuid']
|
||||
fake_lport_status['lr_name'] = fake_lrouter['display_name']
|
||||
self._fake_lrouter_lportstatus_dict[new_uuid] = fake_lport_status
|
||||
return fake_lport
|
||||
|
||||
def _add_securityprofile(self, body):
|
||||
@ -117,29 +197,91 @@ class FakeClient:
|
||||
fake_securityprofile)
|
||||
return fake_securityprofile
|
||||
|
||||
def _add_lrouter_nat(self, body, lr_uuid):
|
||||
fake_nat = json.loads(body)
|
||||
new_uuid = uuidutils.generate_uuid()
|
||||
fake_nat['uuid'] = new_uuid
|
||||
fake_nat['lr_uuid'] = lr_uuid
|
||||
self._fake_lrouter_nat_dict[fake_nat['uuid']] = fake_nat
|
||||
if 'match' in fake_nat:
|
||||
match_json = json.dumps(fake_nat['match'])
|
||||
fake_nat['match_json'] = match_json
|
||||
return fake_nat
|
||||
|
||||
def _build_relation(self, src, dst, resource_type, relation):
|
||||
if not relation in self.MANAGED_RELATIONS[resource_type]:
|
||||
return # Relation is not desired in output
|
||||
if not '_relations' in src or not src['_relations'].get(relation):
|
||||
return # Item does not have relation
|
||||
relation_data = src['_relations'].get(relation)
|
||||
dst_relations = dst.get('_relations')
|
||||
if not dst_relations:
|
||||
dst_relations = {}
|
||||
dst_relations[relation] = relation_data
|
||||
|
||||
def _fill_attachment(self, att_data, ls_uuid=None,
|
||||
lr_uuid=None, lp_uuid=None):
|
||||
new_data = att_data.copy()
|
||||
for k in ('ls_uuid', 'lr_uuid', 'lp_uuid'):
|
||||
if locals().get(k):
|
||||
new_data[k] = locals()[k]
|
||||
|
||||
def populate_field(field_name):
|
||||
if field_name in att_data:
|
||||
new_data['%s_field' % field_name] = ('"%s" : "%s",'
|
||||
% (field_name,
|
||||
att_data[field_name]))
|
||||
del new_data[field_name]
|
||||
else:
|
||||
new_data['%s_field' % field_name] = ""
|
||||
|
||||
for field in ['vif_uuid', 'peer_port_href', 'peer_port_uuid']:
|
||||
populate_field(field)
|
||||
return new_data
|
||||
|
||||
def _get_resource_type(self, path):
|
||||
uri_split = path.split('/')
|
||||
resource_type = ('status' in uri_split and
|
||||
'lport' in uri_split and 'lportstatus'
|
||||
or 'lport' in uri_split and 'lport'
|
||||
or 'lswitch' in uri_split and 'lswitch' or
|
||||
'security-profile' in uri_split and 'securityprofile')
|
||||
switch_uuid = ('lswitch' in uri_split and
|
||||
len(uri_split) > 3 and uri_split[3])
|
||||
port_uuid = ('lport' in uri_split and
|
||||
len(uri_split) > 5 and uri_split[5])
|
||||
securityprofile_uuid = ('security-profile' in uri_split and
|
||||
len(uri_split) > 3 and uri_split[3])
|
||||
return (resource_type, switch_uuid, port_uuid, securityprofile_uuid)
|
||||
"""
|
||||
Identifies resource type and relevant uuids in the uri
|
||||
|
||||
/ws.v1/lswitch/xxx
|
||||
/ws.v1/lswitch/xxx/status
|
||||
/ws.v1/lswitch/xxx/lport/yyy
|
||||
/ws.v1/lswitch/xxx/lport/yyy/status
|
||||
/ws.v1/lrouter/zzz
|
||||
/ws.v1/lrouter/zzz/status
|
||||
/ws.v1/lrouter/zzz/lport/www
|
||||
/ws.v1/lrouter/zzz/lport/www/status
|
||||
"""
|
||||
# The first element will always be 'ws.v1' - so we just discard it
|
||||
uri_split = path.split('/')[1:]
|
||||
# parse uri_split backwards
|
||||
suffix = ""
|
||||
idx = len(uri_split) - 1
|
||||
if 'status' in uri_split[idx]:
|
||||
suffix = "status"
|
||||
idx = idx - 1
|
||||
elif 'attachment' in uri_split[idx]:
|
||||
suffix = "attachment"
|
||||
idx = idx - 1
|
||||
# then check if we have an uuid
|
||||
uuids = []
|
||||
if uri_split[idx].replace('-', '') not in self.RESOURCES:
|
||||
uuids.append(uri_split[idx])
|
||||
idx = idx - 1
|
||||
resource_type = "%s%s" % (uri_split[idx], suffix)
|
||||
if idx > 1:
|
||||
uuids.insert(0, uri_split[idx - 1])
|
||||
resource_type = "%s_%s" % (uri_split[idx - 2], resource_type)
|
||||
return (resource_type.replace('-', ''), uuids)
|
||||
|
||||
def _list(self, resource_type, response_file,
|
||||
switch_uuid=None, query=None):
|
||||
parent_uuid=None, query=None, relations=None):
|
||||
(tag_filter, attr_filter) = self._get_filters(query)
|
||||
with open("%s/%s" % (self.fake_files_path, response_file)) as f:
|
||||
response_template = f.read()
|
||||
res_dict = getattr(self, '_fake_%s_dict' % resource_type)
|
||||
if switch_uuid == "*":
|
||||
switch_uuid = None
|
||||
if parent_uuid == '*':
|
||||
parent_uuid = None
|
||||
|
||||
def _attr_match(res_uuid):
|
||||
if not attr_filter:
|
||||
@ -158,16 +300,49 @@ class FakeClient:
|
||||
for x in res_dict[res_uuid]['tags']])
|
||||
|
||||
def _lswitch_match(res_uuid):
|
||||
if (not switch_uuid or
|
||||
res_dict[res_uuid].get('ls_uuid') == switch_uuid):
|
||||
# verify that the switch exist
|
||||
if parent_uuid and not parent_uuid in self._fake_lswitch_dict:
|
||||
raise Exception(_("lswitch:%s not found" % parent_uuid))
|
||||
if (not parent_uuid
|
||||
or res_dict[res_uuid].get('ls_uuid') == parent_uuid):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _lrouter_match(res_uuid):
|
||||
# verify that the router exist
|
||||
if parent_uuid and not parent_uuid in self._fake_lrouter_dict:
|
||||
raise Exception(_("lrouter:%s not found" % parent_uuid))
|
||||
if (not parent_uuid or
|
||||
res_dict[res_uuid].get('lr_uuid') == parent_uuid):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _build_item(resource):
|
||||
item = json.loads(response_template % resource)
|
||||
if relations:
|
||||
for relation in relations:
|
||||
self._build_relation(resource, item,
|
||||
resource_type, relation)
|
||||
return item
|
||||
|
||||
for item in res_dict.itervalues():
|
||||
if 'tags' in item:
|
||||
item['tags_json'] = json.dumps(item['tags'])
|
||||
items = [json.loads(response_template % res_dict[res_uuid])
|
||||
if resource_type in (self.LSWITCH_LPORT_RESOURCE,
|
||||
self.LSWITCH_LPORT_ATT,
|
||||
self.LSWITCH_LPORT_STATUS):
|
||||
parent_func = _lswitch_match
|
||||
elif resource_type in (self.LROUTER_LPORT_RESOURCE,
|
||||
self.LROUTER_LPORT_ATT,
|
||||
self.LROUTER_NAT_RESOURCE,
|
||||
self.LROUTER_LPORT_STATUS):
|
||||
parent_func = _lrouter_match
|
||||
else:
|
||||
parent_func = lambda x: True
|
||||
|
||||
items = [_build_item(res_dict[res_uuid])
|
||||
for res_uuid in res_dict
|
||||
if (_lswitch_match(res_uuid) and
|
||||
if (parent_func(res_uuid) and
|
||||
_tag_match(res_uuid) and
|
||||
_attr_match(res_uuid))]
|
||||
|
||||
@ -175,8 +350,8 @@ class FakeClient:
|
||||
'result_count': len(items)})
|
||||
|
||||
def _show(self, resource_type, response_file,
|
||||
switch_uuid, port_uuid=None):
|
||||
target_uuid = port_uuid or switch_uuid
|
||||
uuid1, uuid2=None, relations=None):
|
||||
target_uuid = uuid2 or uuid1
|
||||
with open("%s/%s" % (self.fake_files_path, response_file)) as f:
|
||||
response_template = f.read()
|
||||
res_dict = getattr(self, '_fake_%s_dict' % resource_type)
|
||||
@ -194,32 +369,33 @@ class FakeClient:
|
||||
def handle_get(self, url):
|
||||
#TODO(salvatore-orlando): handle field selection
|
||||
parsedurl = urlparse.urlparse(url)
|
||||
(res_type, s_uuid, p_uuid, sec_uuid) = self._get_resource_type(
|
||||
parsedurl.path)
|
||||
(res_type, uuids) = self._get_resource_type(parsedurl.path)
|
||||
relations = urlparse.parse_qs(parsedurl.query).get('relations')
|
||||
response_file = self.FAKE_GET_RESPONSES.get(res_type)
|
||||
if not response_file:
|
||||
raise Exception("resource not found")
|
||||
if res_type == 'lport':
|
||||
if p_uuid:
|
||||
return self._show(res_type, response_file, s_uuid, p_uuid)
|
||||
if 'lport' in res_type or 'nat' in res_type:
|
||||
if len(uuids) > 1:
|
||||
return self._show(res_type, response_file, uuids[0],
|
||||
uuids[1], relations=relations)
|
||||
else:
|
||||
return self._list(res_type, response_file, s_uuid,
|
||||
query=parsedurl.query)
|
||||
elif res_type == 'lportstatus':
|
||||
return self._show(res_type, response_file, s_uuid, p_uuid)
|
||||
elif res_type == 'lswitch':
|
||||
if s_uuid:
|
||||
return self._show(res_type, response_file, s_uuid)
|
||||
return self._list(res_type, response_file, uuids[0],
|
||||
query=parsedurl.query, relations=relations)
|
||||
elif ('lswitch' in res_type or 'lrouter' in res_type
|
||||
or self.SECPROF_RESOURCE in res_type):
|
||||
if len(uuids) > 0:
|
||||
return self._show(res_type, response_file, uuids[0],
|
||||
relations=relations)
|
||||
else:
|
||||
return self._list(res_type, response_file,
|
||||
query=parsedurl.query)
|
||||
query=parsedurl.query,
|
||||
relations=relations)
|
||||
else:
|
||||
raise Exception("unknown resource:%s" % res_type)
|
||||
|
||||
def handle_post(self, url, body):
|
||||
parsedurl = urlparse.urlparse(url)
|
||||
(res_type, s_uuid, _p, sec_uuid) = self._get_resource_type(
|
||||
parsedurl.path)
|
||||
(res_type, uuids) = self._get_resource_type(parsedurl.path)
|
||||
response_file = self.FAKE_POST_RESPONSES.get(res_type)
|
||||
if not response_file:
|
||||
raise Exception("resource not found")
|
||||
@ -227,37 +403,76 @@ class FakeClient:
|
||||
response_template = f.read()
|
||||
add_resource = getattr(self, '_add_%s' % res_type)
|
||||
args = [body]
|
||||
if s_uuid:
|
||||
args.append(s_uuid)
|
||||
if len(uuids):
|
||||
args.append(uuids[0])
|
||||
response = response_template % add_resource(*args)
|
||||
return response
|
||||
|
||||
def handle_put(self, url, body):
|
||||
parsedurl = urlparse.urlparse(url)
|
||||
(res_type, s_uuid, p_uuid, sec_uuid) = self._get_resource_type(
|
||||
parsedurl.path)
|
||||
target_uuid = p_uuid or s_uuid or sec_uuid
|
||||
(res_type, uuids) = self._get_resource_type(parsedurl.path)
|
||||
response_file = self.FAKE_PUT_RESPONSES.get(res_type)
|
||||
if not response_file:
|
||||
raise Exception("resource not found")
|
||||
with open("%s/%s" % (self.fake_files_path, response_file)) as f:
|
||||
response_template = f.read()
|
||||
# Manage attachment operations
|
||||
is_attachment = False
|
||||
if res_type.endswith('attachment'):
|
||||
is_attachment = True
|
||||
res_type = res_type[:res_type.index('attachment')]
|
||||
res_dict = getattr(self, '_fake_%s_dict' % res_type)
|
||||
resource = res_dict[target_uuid]
|
||||
resource.update(json.loads(body))
|
||||
response = response_template % resource
|
||||
resource = res_dict[uuids[-1]]
|
||||
if not is_attachment:
|
||||
resource.update(json.loads(body))
|
||||
else:
|
||||
relations = resource.get("_relations")
|
||||
if not relations:
|
||||
relations = {}
|
||||
relations['LogicalPortAttachment'] = json.loads(body)
|
||||
resource['_relations'] = relations
|
||||
body_2 = json.loads(body)
|
||||
if body_2['type'] == "PatchAttachment":
|
||||
# We need to do a trick here
|
||||
if self.LROUTER_RESOURCE in res_type:
|
||||
res_type_2 = res_type.replace(self.LROUTER_RESOURCE,
|
||||
self.LSWITCH_RESOURCE)
|
||||
elif self.LSWITCH_RESOURCE in res_type:
|
||||
res_type_2 = res_type.replace(self.LSWITCH_RESOURCE,
|
||||
self.LROUTER_RESOURCE)
|
||||
res_dict_2 = getattr(self, '_fake_%s_dict' % res_type_2)
|
||||
body_2['peer_port_uuid'] = uuids[-1]
|
||||
resource_2 = res_dict_2[json.loads(body)['peer_port_uuid']]
|
||||
relations_2 = resource_2.get("_relations")
|
||||
if not relations_2:
|
||||
relations_2 = {}
|
||||
relations_2['LogicalPortAttachment'] = body_2
|
||||
resource_2['_relations'] = relations_2
|
||||
elif body_2['type'] == "L3GatewayAttachment":
|
||||
resource['attachment_gwsvc_uuid'] = (
|
||||
body_2['l3_gateway_service_uuid'])
|
||||
if not is_attachment:
|
||||
response = response_template % resource
|
||||
else:
|
||||
if res_type == self.LROUTER_LPORT_RESOURCE:
|
||||
lr_uuid = uuids[0]
|
||||
ls_uuid = None
|
||||
elif res_type == self.LSWITCH_LPORT_RESOURCE:
|
||||
ls_uuid = uuids[0]
|
||||
lr_uuid = None
|
||||
lp_uuid = uuids[1]
|
||||
response = response_template % self._fill_attachment(
|
||||
json.loads(body), ls_uuid, lr_uuid, lp_uuid)
|
||||
return response
|
||||
|
||||
def handle_delete(self, url):
|
||||
parsedurl = urlparse.urlparse(url)
|
||||
(res_type, s_uuid, p_uuid, sec_uuid) = self._get_resource_type(
|
||||
parsedurl.path)
|
||||
target_uuid = p_uuid or s_uuid or sec_uuid
|
||||
(res_type, uuids) = self._get_resource_type(parsedurl.path)
|
||||
response_file = self.FAKE_PUT_RESPONSES.get(res_type)
|
||||
if not response_file:
|
||||
raise Exception("resource not found")
|
||||
res_dict = getattr(self, '_fake_%s_dict' % res_type)
|
||||
del res_dict[target_uuid]
|
||||
del res_dict[uuids[-1]]
|
||||
return ""
|
||||
|
||||
def fake_request(self, *args, **kwargs):
|
||||
@ -267,5 +482,8 @@ class FakeClient:
|
||||
|
||||
def reset_all(self):
|
||||
self._fake_lswitch_dict.clear()
|
||||
self._fake_lport_dict.clear()
|
||||
self._fake_lportstatus_dict.clear()
|
||||
self._fake_lrouter_dict.clear()
|
||||
self._fake_lswitch_lport_dict.clear()
|
||||
self._fake_lrouter_lport_dict.clear()
|
||||
self._fake_lswitch_lportstatus_dict.clear()
|
||||
self._fake_lrouter_lportstatus_dict.clear()
|
||||
|
30
quantum/tests/unit/nicira/test_defaults.py
Normal file
30
quantum/tests/unit/nicira/test_defaults.py
Normal file
@ -0,0 +1,30 @@
|
||||
# Copyright 2013 Nicira Networks, Inc.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import unittest2 as unittest
|
||||
|
||||
from quantum.openstack.common import cfg
|
||||
from quantum.plugins.nicira.nicira_nvp_plugin.common import config
|
||||
|
||||
|
||||
class ConfigurationTest(unittest.TestCase):
|
||||
|
||||
def test_defaults(self):
|
||||
self.assertEqual('sqlite://', cfg.CONF.DATABASE.sql_connection)
|
||||
self.assertEqual(-1, cfg.CONF.DATABASE.sql_max_retries)
|
||||
self.assertEqual(2, cfg.CONF.DATABASE.reconnect_interval)
|
||||
self.assertEqual(64, cfg.CONF.NVP.max_lp_per_bridged_ls)
|
||||
self.assertEqual(256, cfg.CONF.NVP.max_lp_per_overlay_ls)
|
||||
self.assertEqual(5, cfg.CONF.NVP.concurrent_connections)
|
@ -13,6 +13,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import contextlib
|
||||
import logging
|
||||
import os
|
||||
|
||||
@ -30,6 +31,7 @@ from quantum.tests.unit.nicira import fake_nvpapiclient
|
||||
import quantum.tests.unit.test_db_plugin as test_plugin
|
||||
import quantum.tests.unit.test_extension_portsecurity as psec
|
||||
import quantum.tests.unit.test_extension_security_group as ext_sg
|
||||
import quantum.tests.unit.test_l3_plugin as test_l3_plugin
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
NICIRA_PKG_PATH = 'quantum.plugins.nicira.nicira_nvp_plugin'
|
||||
@ -174,6 +176,18 @@ class TestNiciraNetworksV2(test_plugin.TestNetworksV2,
|
||||
self._test_create_bridge_network(vlan_id=5000)
|
||||
self.assertEquals(ctx_manager.exception.code, 400)
|
||||
|
||||
def test_list_networks_filter_by_id(self):
|
||||
# We add this unit test to cover some logic specific to the
|
||||
# nvp plugin
|
||||
with contextlib.nested(self.network(name='net1'),
|
||||
self.network(name='net2')) as (net1, net2):
|
||||
query_params = 'id=%s' % net1['network']['id']
|
||||
self._test_list_resources('network', [net1],
|
||||
query_params=query_params)
|
||||
query_params += '&id=%s' % net2['network']['id']
|
||||
self._test_list_resources('network', [net1, net2],
|
||||
query_params=query_params)
|
||||
|
||||
|
||||
class NiciraPortSecurityTestCase(psec.PortSecurityDBTestCase):
|
||||
|
||||
@ -235,3 +249,12 @@ class NiciraSecurityGroupsTestCase(ext_sg.SecurityGroupDBTestCase):
|
||||
class TestNiciraSecurityGroup(ext_sg.TestSecurityGroups,
|
||||
NiciraSecurityGroupsTestCase):
|
||||
pass
|
||||
|
||||
|
||||
class TestNiciraL3NatTestCase(test_l3_plugin.L3NatDBTestCase,
|
||||
NiciraPluginV2TestCase):
|
||||
|
||||
def test_floatingip_with_assoc_fails(self):
|
||||
self._test_floatingip_with_assoc_fails(
|
||||
'quantum.plugins.nicira.nicira_nvp_plugin.'
|
||||
'QuantumPlugin.NvpPluginV2')
|
||||
|
@ -324,12 +324,17 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
super(L3NatDBTestCase, self).tearDown()
|
||||
|
||||
def _create_router(self, fmt, tenant_id, name=None,
|
||||
admin_state_up=None, set_context=False):
|
||||
admin_state_up=None, set_context=False,
|
||||
arg_list=None, **kwargs):
|
||||
data = {'router': {'tenant_id': tenant_id}}
|
||||
if name:
|
||||
data['router']['name'] = name
|
||||
if admin_state_up:
|
||||
data['router']['admin_state_up'] = admin_state_up
|
||||
for arg in (('admin_state_up', 'tenant_id') + (arg_list or ())):
|
||||
# Arg must be present and not empty
|
||||
if arg in kwargs and kwargs[arg]:
|
||||
data['router'][arg] = kwargs[arg]
|
||||
router_req = self.new_create_request('routers', data, fmt)
|
||||
if set_context and tenant_id:
|
||||
# create a specific auth context for this request
|
||||
@ -1080,7 +1085,7 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
self._show('floatingips', fip['floatingip']['id'],
|
||||
expected_code=exc.HTTPNotFound.code)
|
||||
|
||||
def test_floatingip_with_assoc_fails(self):
|
||||
def _test_floatingip_with_assoc_fails(self, plugin_class):
|
||||
with self.subnet(cidr='200.0.0.1/24') as public_sub:
|
||||
self._set_net_external(public_sub['subnet']['network_id'])
|
||||
with self.port() as private_port:
|
||||
@ -1093,9 +1098,8 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
self._router_interface_action('add', r['router']['id'],
|
||||
private_sub['subnet']['id'],
|
||||
None)
|
||||
PLUGIN_CLASS = 'quantum.db.l3_db.L3_NAT_db_mixin'
|
||||
METHOD = PLUGIN_CLASS + '._update_fip_assoc'
|
||||
with mock.patch(METHOD) as pl:
|
||||
method = plugin_class + '._update_fip_assoc'
|
||||
with mock.patch(method) as pl:
|
||||
pl.side_effect = q_exc.BadRequest(
|
||||
resource='floatingip',
|
||||
msg='fake_error')
|
||||
@ -1117,6 +1121,10 @@ class L3NatDBTestCase(test_db_plugin.QuantumDbPluginV2TestCase):
|
||||
private_sub['subnet']['id'],
|
||||
None)
|
||||
|
||||
def test_floatingip_with_assoc_fails(self):
|
||||
self._test_floatingip_with_assoc_fails(
|
||||
'quantum.db.l3_db.L3_NAT_db_mixin')
|
||||
|
||||
def test_floatingip_update(self):
|
||||
with self.port() as p:
|
||||
private_sub = {'subnet': {'id':
|
||||
|
Loading…
Reference in New Issue
Block a user