Add NVP Security group support
Implements blueprint security-groups-nvp Change-Id: Idfa7a756c7a2845e9aa9e7de4c7bceeec94b036f
This commit is contained in:
parent
af33f1d051
commit
c0e9c2791f
@ -34,6 +34,9 @@ INTERFACE_KEY = '_interfaces'
|
|||||||
IPv4 = 'IPv4'
|
IPv4 = 'IPv4'
|
||||||
IPv6 = 'IPv6'
|
IPv6 = 'IPv6'
|
||||||
|
|
||||||
|
UDP_PROTOCOL = 17
|
||||||
|
DHCP_RESPONSE_PORT = 68
|
||||||
|
|
||||||
EXT_NS = '_extension_ns'
|
EXT_NS = '_extension_ns'
|
||||||
XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
|
XML_NS_V20 = 'http://openstack.org/quantum/api/v2.0'
|
||||||
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
|
XSI_NAMESPACE = "http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
@ -30,7 +30,8 @@ down_revision = '48b6f43f7471'
|
|||||||
# Change to ['*'] if this migration applies to all plugins
|
# Change to ['*'] if this migration applies to all plugins
|
||||||
|
|
||||||
migration_for_plugins = [
|
migration_for_plugins = [
|
||||||
'quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2'
|
'quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2',
|
||||||
|
'quantum.plugins.nicira.nicira_nvp_plugin.QuantumPlugin.NvpPluginV2'
|
||||||
]
|
]
|
||||||
|
|
||||||
from alembic import op
|
from alembic import op
|
||||||
|
@ -25,7 +25,7 @@ import logging
|
|||||||
|
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from quantum.api.v2 import attributes
|
from quantum.api.v2 import attributes as attr
|
||||||
from quantum.api.v2 import base
|
from quantum.api.v2 import base
|
||||||
from quantum.common import constants
|
from quantum.common import constants
|
||||||
from quantum.common import exceptions as q_exc
|
from quantum.common import exceptions as q_exc
|
||||||
@ -37,10 +37,14 @@ from quantum.db import dhcp_rpc_base
|
|||||||
from quantum.db import portsecurity_db
|
from quantum.db import portsecurity_db
|
||||||
# NOTE: quota_db cannot be removed, it is for db model
|
# NOTE: quota_db cannot be removed, it is for db model
|
||||||
from quantum.db import quota_db
|
from quantum.db import quota_db
|
||||||
|
from quantum.db import securitygroups_db
|
||||||
from quantum.extensions import portsecurity as psec
|
from quantum.extensions import portsecurity as psec
|
||||||
from quantum.extensions import providernet as pnet
|
from quantum.extensions import providernet as pnet
|
||||||
|
from quantum.extensions import securitygroup as ext_sg
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.openstack.common import rpc
|
from quantum.openstack.common import rpc
|
||||||
|
from quantum.plugins.nicira.nicira_nvp_plugin.common import (securitygroups
|
||||||
|
as nvp_sec)
|
||||||
from quantum import policy
|
from quantum import policy
|
||||||
from quantum.plugins.nicira.nicira_nvp_plugin.common import config
|
from quantum.plugins.nicira.nicira_nvp_plugin.common import config
|
||||||
from quantum.plugins.nicira.nicira_nvp_plugin.common import (exceptions
|
from quantum.plugins.nicira.nicira_nvp_plugin.common import (exceptions
|
||||||
@ -108,13 +112,18 @@ class NVPRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin):
|
|||||||
|
|
||||||
|
|
||||||
class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
||||||
portsecurity_db.PortSecurityDbMixin):
|
portsecurity_db.PortSecurityDbMixin,
|
||||||
|
securitygroups_db.SecurityGroupDbMixin,
|
||||||
|
nvp_sec.NVPSecurityGroups):
|
||||||
"""
|
"""
|
||||||
NvpPluginV2 is a Quantum plugin that provides L2 Virtual Network
|
NvpPluginV2 is a Quantum plugin that provides L2 Virtual Network
|
||||||
functionality using NVP.
|
functionality using NVP.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
supported_extension_aliases = ["provider", "quotas", "port-security"]
|
supported_extension_aliases = ["provider", "quotas", "port-security",
|
||||||
|
"security-group"]
|
||||||
|
__native_bulk_support = True
|
||||||
|
|
||||||
# Default controller cluster
|
# Default controller cluster
|
||||||
default_cluster = None
|
default_cluster = None
|
||||||
|
|
||||||
@ -236,9 +245,9 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
network_type = attrs.get(pnet.NETWORK_TYPE)
|
network_type = attrs.get(pnet.NETWORK_TYPE)
|
||||||
physical_network = attrs.get(pnet.PHYSICAL_NETWORK)
|
physical_network = attrs.get(pnet.PHYSICAL_NETWORK)
|
||||||
segmentation_id = attrs.get(pnet.SEGMENTATION_ID)
|
segmentation_id = attrs.get(pnet.SEGMENTATION_ID)
|
||||||
network_type_set = attributes.is_attr_set(network_type)
|
network_type_set = attr.is_attr_set(network_type)
|
||||||
physical_network_set = attributes.is_attr_set(physical_network)
|
physical_network_set = attr.is_attr_set(physical_network)
|
||||||
segmentation_id_set = attributes.is_attr_set(segmentation_id)
|
segmentation_id_set = attr.is_attr_set(segmentation_id)
|
||||||
if not (network_type_set or physical_network_set or
|
if not (network_type_set or physical_network_set or
|
||||||
segmentation_id_set):
|
segmentation_id_set):
|
||||||
return
|
return
|
||||||
@ -345,18 +354,19 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
|
|
||||||
def create_network(self, context, network):
|
def create_network(self, context, network):
|
||||||
net_data = network['network'].copy()
|
net_data = network['network'].copy()
|
||||||
|
tenant_id = self._get_tenant_id_for_create(context, net_data)
|
||||||
|
self._ensure_default_security_group(context, tenant_id)
|
||||||
# Process the provider network extension
|
# Process the provider network extension
|
||||||
self._handle_provider_create(context, net_data)
|
self._handle_provider_create(context, net_data)
|
||||||
# Replace ATTR_NOT_SPECIFIED with None before sending to NVP
|
# Replace ATTR_NOT_SPECIFIED with None before sending to NVP
|
||||||
for attr, value in network['network'].iteritems():
|
for key, value in network['network'].iteritems():
|
||||||
if value is attributes.ATTR_NOT_SPECIFIED:
|
if value is attr.ATTR_NOT_SPECIFIED:
|
||||||
net_data[attr] = None
|
net_data[key] = None
|
||||||
# FIXME(arosen) implement admin_state_up = False in NVP
|
# FIXME(arosen) implement admin_state_up = False in NVP
|
||||||
if net_data['admin_state_up'] is False:
|
if net_data['admin_state_up'] is False:
|
||||||
LOG.warning(_("Network with admin_state_up=False are not yet "
|
LOG.warning(_("Network with admin_state_up=False are not yet "
|
||||||
"supported by this plugin. Ignoring setting for "
|
"supported by this plugin. Ignoring setting for "
|
||||||
"network %s"), net_data.get('name', '<unknown>'))
|
"network %s"), net_data.get('name', '<unknown>'))
|
||||||
tenant_id = self._get_tenant_id_for_create(context, net_data)
|
|
||||||
target_cluster = self._find_target_cluster(net_data)
|
target_cluster = self._find_target_cluster(net_data)
|
||||||
nvp_binding_type = net_data.get(pnet.NETWORK_TYPE)
|
nvp_binding_type = net_data.get(pnet.NETWORK_TYPE)
|
||||||
if nvp_binding_type in ('flat', 'vlan'):
|
if nvp_binding_type in ('flat', 'vlan'):
|
||||||
@ -544,6 +554,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
context, filters)
|
context, filters)
|
||||||
for quantum_lport in quantum_lports:
|
for quantum_lport in quantum_lports:
|
||||||
self._extend_port_port_security_dict(context, quantum_lport)
|
self._extend_port_port_security_dict(context, quantum_lport)
|
||||||
|
self._extend_port_dict_security_group(context, quantum_lport)
|
||||||
|
|
||||||
vm_filter = ""
|
vm_filter = ""
|
||||||
tenant_filter = ""
|
tenant_filter = ""
|
||||||
@ -638,7 +649,7 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
# ATTR_NOT_SPECIFIED is for the case where a port is created on a
|
# ATTR_NOT_SPECIFIED is for the case where a port is created on a
|
||||||
# shared network that is not owned by the tenant.
|
# shared network that is not owned by the tenant.
|
||||||
# TODO(arosen) fix policy engine to do this for us automatically.
|
# TODO(arosen) fix policy engine to do this for us automatically.
|
||||||
if attributes.is_attr_set(port['port'].get(psec.PORTSECURITY)):
|
if attr.is_attr_set(port['port'].get(psec.PORTSECURITY)):
|
||||||
self._enforce_set_auth(context, port,
|
self._enforce_set_auth(context, port,
|
||||||
self.port_security_enabled_create)
|
self.port_security_enabled_create)
|
||||||
port_data = port['port']
|
port_data = port['port']
|
||||||
@ -653,6 +664,15 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
context, port_data)
|
context, port_data)
|
||||||
port_data[psec.PORTSECURITY] = port_security
|
port_data[psec.PORTSECURITY] = port_security
|
||||||
self._process_port_security_create(context, port_data)
|
self._process_port_security_create(context, port_data)
|
||||||
|
# security group extension checks
|
||||||
|
if port_security and has_ip:
|
||||||
|
self._ensure_default_security_group_on_port(context, port)
|
||||||
|
elif attr.is_attr_set(port_data.get(ext_sg.SECURITYGROUPS)):
|
||||||
|
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
|
||||||
|
port_data[ext_sg.SECURITYGROUPS] = (
|
||||||
|
self._get_security_groups_on_port(context, port))
|
||||||
|
self._process_port_create_security_group(
|
||||||
|
context, quantum_db['id'], port_data[ext_sg.SECURITYGROUPS])
|
||||||
# provider networking extension checks
|
# provider networking extension checks
|
||||||
# Fetch the network and network binding from Quantum db
|
# Fetch the network and network binding from Quantum db
|
||||||
network = self._get_network(context, port_data['network_id'])
|
network = self._get_network(context, port_data['network_id'])
|
||||||
@ -681,7 +701,8 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
port_data['admin_state_up'],
|
port_data['admin_state_up'],
|
||||||
port_data['mac_address'],
|
port_data['mac_address'],
|
||||||
port_data['fixed_ips'],
|
port_data['fixed_ips'],
|
||||||
port_data[psec.PORTSECURITY])
|
port_data[psec.PORTSECURITY],
|
||||||
|
port_data[ext_sg.SECURITYGROUPS])
|
||||||
# Get NVP ls uuid for quantum network
|
# Get NVP ls uuid for quantum network
|
||||||
nvplib.plug_interface(cluster, selected_lswitch['uuid'],
|
nvplib.plug_interface(cluster, selected_lswitch['uuid'],
|
||||||
lport['uuid'], "VifAttachment",
|
lport['uuid'], "VifAttachment",
|
||||||
@ -703,27 +724,55 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
"%(tenant_id)s: (%(id)s)"), port_data)
|
"%(tenant_id)s: (%(id)s)"), port_data)
|
||||||
|
|
||||||
self._extend_port_port_security_dict(context, port_data)
|
self._extend_port_port_security_dict(context, port_data)
|
||||||
|
self._extend_port_dict_security_group(context, port_data)
|
||||||
return port_data
|
return port_data
|
||||||
|
|
||||||
def update_port(self, context, id, port):
|
def update_port(self, context, id, port):
|
||||||
self._enforce_set_auth(context, port,
|
self._enforce_set_auth(context, port,
|
||||||
self.port_security_enabled_update)
|
self.port_security_enabled_update)
|
||||||
tenant_id = self._get_tenant_id_for_create(context, port)
|
tenant_id = self._get_tenant_id_for_create(context, port)
|
||||||
|
delete_security_groups = self._check_update_deletes_security_groups(
|
||||||
|
port)
|
||||||
|
has_security_groups = self._check_update_has_security_groups(port)
|
||||||
with context.session.begin(subtransactions=True):
|
with context.session.begin(subtransactions=True):
|
||||||
ret_port = super(NvpPluginV2, self).update_port(
|
ret_port = super(NvpPluginV2, self).update_port(
|
||||||
context, id, port)
|
context, id, port)
|
||||||
# copy values over
|
# copy values over
|
||||||
ret_port.update(port['port'])
|
ret_port.update(port['port'])
|
||||||
|
tenant_id = self._get_tenant_id_for_create(context, ret_port)
|
||||||
|
|
||||||
# Handle port security
|
# populate port_security setting
|
||||||
if psec.PORTSECURITY in port['port']:
|
if psec.PORTSECURITY not in port['port']:
|
||||||
self._update_port_security_binding(
|
|
||||||
context, id, ret_port[psec.PORTSECURITY])
|
|
||||||
# populate with value
|
|
||||||
else:
|
|
||||||
ret_port[psec.PORTSECURITY] = self._get_port_security_binding(
|
ret_port[psec.PORTSECURITY] = self._get_port_security_binding(
|
||||||
context, id)
|
context, id)
|
||||||
|
|
||||||
|
has_ip = self._ip_on_port(ret_port)
|
||||||
|
# checks if security groups were updated adding/modifying
|
||||||
|
# security groups, port security is set and port has ip
|
||||||
|
if not (has_ip and ret_port[psec.PORTSECURITY]):
|
||||||
|
if has_security_groups:
|
||||||
|
raise psec.PortSecurityAndIPRequiredForSecurityGroups()
|
||||||
|
# Update did not have security groups passed in. Check
|
||||||
|
# that port does not have any security groups already on it.
|
||||||
|
filters = {'port_id': [id]}
|
||||||
|
security_groups = (
|
||||||
|
super(NvpPluginV2, self)._get_port_security_group_bindings(
|
||||||
|
context, filters)
|
||||||
|
)
|
||||||
|
if security_groups and not delete_security_groups:
|
||||||
|
raise psec.PortSecurityPortHasSecurityGroup()
|
||||||
|
|
||||||
|
if (delete_security_groups or has_security_groups):
|
||||||
|
# delete the port binding and read it with the new rules.
|
||||||
|
self._delete_port_security_group_bindings(context, id)
|
||||||
|
sgids = self._get_security_groups_on_port(context, port)
|
||||||
|
self._process_port_create_security_group(context, id, sgids)
|
||||||
|
|
||||||
|
if psec.PORTSECURITY in port['port']:
|
||||||
|
self._update_port_security_binding(
|
||||||
|
context, id, ret_port[psec.PORTSECURITY])
|
||||||
|
self._extend_port_port_security_dict(context, ret_port)
|
||||||
|
self._extend_port_dict_security_group(context, ret_port)
|
||||||
port_nvp, cluster = (
|
port_nvp, cluster = (
|
||||||
nvplib.get_port_by_quantum_tag(self.clusters.itervalues(),
|
nvplib.get_port_by_quantum_tag(self.clusters.itervalues(),
|
||||||
ret_port["network_id"], id))
|
ret_port["network_id"], id))
|
||||||
@ -734,7 +783,8 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
ret_port['admin_state_up'],
|
ret_port['admin_state_up'],
|
||||||
ret_port['mac_address'],
|
ret_port['mac_address'],
|
||||||
ret_port['fixed_ips'],
|
ret_port['fixed_ips'],
|
||||||
ret_port[psec.PORTSECURITY])
|
ret_port[psec.PORTSECURITY],
|
||||||
|
ret_port[ext_sg.SECURITYGROUPS])
|
||||||
|
|
||||||
# Update the port status from nvp. If we fail here hide it since
|
# Update the port status from nvp. If we fail here hide it since
|
||||||
# the port was successfully updated but we were not able to retrieve
|
# the port was successfully updated but we were not able to retrieve
|
||||||
@ -763,7 +813,10 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
return super(NvpPluginV2, self).delete_port(context, id)
|
return super(NvpPluginV2, self).delete_port(context, id)
|
||||||
|
|
||||||
def get_port(self, context, id, fields=None):
|
def get_port(self, context, id, fields=None):
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
quantum_db = super(NvpPluginV2, self).get_port(context, id, fields)
|
quantum_db = super(NvpPluginV2, self).get_port(context, id, fields)
|
||||||
|
self._extend_port_port_security_dict(context, quantum_db)
|
||||||
|
self._extend_port_dict_security_group(context, quantum_db)
|
||||||
|
|
||||||
#TODO: pass only the appropriate cluster here
|
#TODO: pass only the appropriate cluster here
|
||||||
#Look for port in all lswitches
|
#Look for port in all lswitches
|
||||||
@ -783,3 +836,124 @@ class NvpPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
|
|
||||||
def get_plugin_version(self):
|
def get_plugin_version(self):
|
||||||
return PLUGIN_VERSION
|
return PLUGIN_VERSION
|
||||||
|
|
||||||
|
def create_security_group(self, context, security_group, default_sg=False):
|
||||||
|
"""Create security group.
|
||||||
|
If default_sg is true that means a we are creating a default security
|
||||||
|
group and we don't need to check if one exists.
|
||||||
|
"""
|
||||||
|
s = security_group.get('security_group')
|
||||||
|
if cfg.CONF.SECURITYGROUP.proxy_mode:
|
||||||
|
if not context.is_admin:
|
||||||
|
raise ext_sg.SecurityGroupProxyModeNotAdmin()
|
||||||
|
elif not s.get('external_id'):
|
||||||
|
raise ext_sg.SecurityGroupProxyMode()
|
||||||
|
elif s.get('external_id'):
|
||||||
|
raise ext_sg.SecurityGroupNotProxyMode()
|
||||||
|
|
||||||
|
tenant_id = self._get_tenant_id_for_create(context, s)
|
||||||
|
if not default_sg and not cfg.CONF.SECURITYGROUP.proxy_mode:
|
||||||
|
self._ensure_default_security_group(context, tenant_id,
|
||||||
|
security_group)
|
||||||
|
if s.get('external_id'):
|
||||||
|
filters = {'external_id': [s.get('external_id')]}
|
||||||
|
security_groups = super(NvpPluginV2, self).get_security_groups(
|
||||||
|
context, filters=filters)
|
||||||
|
if security_groups:
|
||||||
|
raise ext_sg.SecurityGroupAlreadyExists(
|
||||||
|
name=s.get('name', ''), external_id=s.get('external_id'))
|
||||||
|
nvp_secgroup = nvplib.create_security_profile(self.default_cluster,
|
||||||
|
tenant_id, s)
|
||||||
|
security_group['security_group']['id'] = nvp_secgroup['uuid']
|
||||||
|
return super(NvpPluginV2, self).create_security_group(
|
||||||
|
context, security_group, default_sg)
|
||||||
|
|
||||||
|
def delete_security_group(self, context, security_group_id):
|
||||||
|
"""Delete a security group
|
||||||
|
:param security_group_id: security group rule to remove.
|
||||||
|
"""
|
||||||
|
if (cfg.CONF.SECURITYGROUP.proxy_mode and not context.is_admin):
|
||||||
|
raise ext_sg.SecurityGroupProxyModeNotAdmin()
|
||||||
|
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
security_group = super(NvpPluginV2, self).get_security_group(
|
||||||
|
context, security_group_id)
|
||||||
|
if not security_group:
|
||||||
|
raise ext_sg.SecurityGroupNotFound(id=security_group_id)
|
||||||
|
|
||||||
|
if security_group['name'] == 'default':
|
||||||
|
raise ext_sg.SecurityGroupCannotRemoveDefault()
|
||||||
|
|
||||||
|
filters = {'security_group_id': [security_group['id']]}
|
||||||
|
if super(NvpPluginV2, self)._get_port_security_group_bindings(
|
||||||
|
context, filters):
|
||||||
|
raise ext_sg.SecurityGroupInUse(id=security_group['id'])
|
||||||
|
nvplib.delete_security_profile(self.default_cluster,
|
||||||
|
security_group['id'])
|
||||||
|
return super(NvpPluginV2, self).delete_security_group(
|
||||||
|
context, security_group_id)
|
||||||
|
|
||||||
|
def create_security_group_rule(self, context, security_group_rule):
|
||||||
|
"""create a single security group rule"""
|
||||||
|
bulk_rule = {'security_group_rules': [security_group_rule]}
|
||||||
|
return self.create_security_group_rule_bulk(context, bulk_rule)[0]
|
||||||
|
|
||||||
|
def create_security_group_rule_bulk(self, context, security_group_rule):
|
||||||
|
""" create security group rules
|
||||||
|
:param security_group_rule: list of rules to create
|
||||||
|
"""
|
||||||
|
s = security_group_rule.get('security_group_rules')
|
||||||
|
tenant_id = self._get_tenant_id_for_create(context, s)
|
||||||
|
|
||||||
|
# TODO(arosen) is there anyway we could avoid having the update of
|
||||||
|
# the security group rules in nvp outside of this transaction?
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
self._ensure_default_security_group(context, tenant_id)
|
||||||
|
security_group_id = self._validate_security_group_rules(
|
||||||
|
context, security_group_rule)
|
||||||
|
|
||||||
|
# Check to make sure security group exists and retrieve
|
||||||
|
# security_group['id'] needed incase it only has an external_id
|
||||||
|
security_group = super(NvpPluginV2, self).get_security_group(
|
||||||
|
context, security_group_id)
|
||||||
|
|
||||||
|
if not security_group:
|
||||||
|
raise ext_sg.SecurityGroupNotFound(id=security_group_id)
|
||||||
|
# Check for duplicate rules
|
||||||
|
self._check_for_duplicate_rules(context, s)
|
||||||
|
# gather all the existing security group rules since we need all
|
||||||
|
# of them to PUT to NVP.
|
||||||
|
combined_rules = self._merge_security_group_rules_with_current(
|
||||||
|
context, s, security_group['id'])
|
||||||
|
nvplib.update_security_group_rules(self.default_cluster,
|
||||||
|
security_group['id'],
|
||||||
|
combined_rules)
|
||||||
|
return super(
|
||||||
|
NvpPluginV2, self).create_security_group_rule_bulk_native(
|
||||||
|
context, security_group_rule)
|
||||||
|
|
||||||
|
def delete_security_group_rule(self, context, sgrid):
|
||||||
|
""" Delete a security group rule
|
||||||
|
:param sgrid: security group id to remove.
|
||||||
|
"""
|
||||||
|
if (cfg.CONF.SECURITYGROUP.proxy_mode and not context.is_admin):
|
||||||
|
raise ext_sg.SecurityGroupProxyModeNotAdmin()
|
||||||
|
|
||||||
|
with context.session.begin(subtransactions=True):
|
||||||
|
# determine security profile id
|
||||||
|
security_group_rule = (
|
||||||
|
super(NvpPluginV2, self).get_security_group_rule(
|
||||||
|
context, sgrid))
|
||||||
|
if not security_group_rule:
|
||||||
|
raise ext_sg.SecurityGroupRuleNotFound(id=sgrid)
|
||||||
|
|
||||||
|
sgid = security_group_rule['security_group_id']
|
||||||
|
current_rules = self._get_security_group_rules_nvp_format(
|
||||||
|
context, sgid, True)
|
||||||
|
|
||||||
|
self._remove_security_group_with_id_and_id_field(
|
||||||
|
current_rules, sgrid)
|
||||||
|
nvplib.update_security_group_rules(
|
||||||
|
self.default_cluster, sgid, current_rules)
|
||||||
|
return super(NvpPluginV2, self).delete_security_group_rule(context,
|
||||||
|
sgrid)
|
||||||
|
@ -0,0 +1,124 @@
|
|||||||
|
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||||
|
|
||||||
|
# Copyright 2013 Nicira, 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 equired 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: Aaron Rosen, Nicira Networks, Inc.
|
||||||
|
|
||||||
|
from quantum.extensions import securitygroup as ext_sg
|
||||||
|
|
||||||
|
# Protocol number look up for supported protocols
|
||||||
|
protocol_num_look_up = {'tcp': 6, 'icmp': 1, 'udp': 17}
|
||||||
|
|
||||||
|
|
||||||
|
class NVPSecurityGroups(object):
|
||||||
|
|
||||||
|
def _convert_to_nvp_rule(self, rule, with_id=False):
|
||||||
|
"""Converts Quantum API security group rule to NVP API."""
|
||||||
|
nvp_rule = {}
|
||||||
|
params = ['source_ip_prefix', 'protocol',
|
||||||
|
'source_group_id', 'port_range_min',
|
||||||
|
'port_range_max', 'ethertype']
|
||||||
|
if with_id:
|
||||||
|
params.append('id')
|
||||||
|
|
||||||
|
for param in params:
|
||||||
|
value = rule.get(param)
|
||||||
|
if param not in rule:
|
||||||
|
nvp_rule[param] = value
|
||||||
|
elif not value:
|
||||||
|
pass
|
||||||
|
elif param == 'source_ip_prefix':
|
||||||
|
nvp_rule['ip_prefix'] = rule['source_ip_prefix']
|
||||||
|
elif param == 'source_group_id':
|
||||||
|
nvp_rule['profile_uuid'] = rule['source_group_id']
|
||||||
|
elif param == 'protocol':
|
||||||
|
nvp_rule['protocol'] = protocol_num_look_up[rule['protocol']]
|
||||||
|
else:
|
||||||
|
nvp_rule[param] = value
|
||||||
|
return nvp_rule
|
||||||
|
|
||||||
|
def _convert_to_nvp_rules(self, rules, with_id=False):
|
||||||
|
"""Converts a list of Quantum API security group rules to NVP API."""
|
||||||
|
nvp_rules = {'logical_port_ingress_rules': [],
|
||||||
|
'logical_port_egress_rules': []}
|
||||||
|
for direction in ['logical_port_ingress_rules',
|
||||||
|
'logical_port_egress_rules']:
|
||||||
|
for rule in rules[direction]:
|
||||||
|
nvp_rules[direction].append(
|
||||||
|
self._convert_to_nvp_rule(rule, with_id))
|
||||||
|
return nvp_rules
|
||||||
|
|
||||||
|
def _get_security_group_rules_nvp_format(self, context, security_group_id,
|
||||||
|
with_id=False):
|
||||||
|
"""Query quantum db for security group rules. If external_id is
|
||||||
|
provided the external_id will also be returned.
|
||||||
|
"""
|
||||||
|
fields = ['source_ip_prefix', 'source_group_id', 'protocol',
|
||||||
|
'port_range_min', 'port_range_max', 'protocol', 'ethertype']
|
||||||
|
if with_id:
|
||||||
|
fields.append('id')
|
||||||
|
|
||||||
|
filters = {'security_group_id': [security_group_id],
|
||||||
|
'direction': ['ingress']}
|
||||||
|
ingress_rules = self.get_security_group_rules(context, filters, fields)
|
||||||
|
filters = {'security_group_id': [security_group_id],
|
||||||
|
'direction': ['egress']}
|
||||||
|
egress_rules = self.get_security_group_rules(context, filters, fields)
|
||||||
|
rules = {'logical_port_ingress_rules': egress_rules,
|
||||||
|
'logical_port_egress_rules': ingress_rules}
|
||||||
|
return self._convert_to_nvp_rules(rules, with_id)
|
||||||
|
|
||||||
|
def _get_profile_uuid(self, context, source_group_id):
|
||||||
|
"""Return profile id from novas group id. """
|
||||||
|
security_group = self.get_security_group(context, source_group_id)
|
||||||
|
if not security_group:
|
||||||
|
raise ext_sg.SecurityGroupNotFound(id=source_group_id)
|
||||||
|
return security_group['id']
|
||||||
|
|
||||||
|
def _merge_security_group_rules_with_current(self, context, new_rules,
|
||||||
|
security_group_id):
|
||||||
|
merged_rules = self._get_security_group_rules_nvp_format(
|
||||||
|
context, security_group_id)
|
||||||
|
for new_rule in new_rules:
|
||||||
|
rule = new_rule['security_group_rule']
|
||||||
|
rule['security_group_id'] = security_group_id
|
||||||
|
if rule.get('souce_group_id'):
|
||||||
|
rule['source_group_id'] = self._get_profile_uuid(
|
||||||
|
context, rule['source_group_id'])
|
||||||
|
if rule['direction'] == 'ingress':
|
||||||
|
merged_rules['logical_port_egress_rules'].append(
|
||||||
|
self._convert_to_nvp_rule(rule))
|
||||||
|
elif rule['direction'] == 'egress':
|
||||||
|
merged_rules['logical_port_ingress_rules'].append(
|
||||||
|
self._convert_to_nvp_rule(rule))
|
||||||
|
return merged_rules
|
||||||
|
|
||||||
|
def _remove_security_group_with_id_and_id_field(self, rules, rule_id):
|
||||||
|
"""This function receives all of the current rule associated with a
|
||||||
|
security group and then removes the rule that matches the rule_id. In
|
||||||
|
addition it removes the id field in the dict with each rule since that
|
||||||
|
should not be passed to nvp.
|
||||||
|
"""
|
||||||
|
for rule_direction in rules.values():
|
||||||
|
item_to_remove = None
|
||||||
|
for port_rule in rule_direction:
|
||||||
|
if port_rule['id'] == rule_id:
|
||||||
|
item_to_remove = port_rule
|
||||||
|
else:
|
||||||
|
# remove key from dictionary for NVP
|
||||||
|
del port_rule['id']
|
||||||
|
if item_to_remove:
|
||||||
|
rule_direction.remove(item_to_remove)
|
@ -417,7 +417,7 @@ def get_port(cluster, network, port, relations=None):
|
|||||||
|
|
||||||
|
|
||||||
def _configure_extensions(lport_obj, mac_address, fixed_ips,
|
def _configure_extensions(lport_obj, mac_address, fixed_ips,
|
||||||
port_security_enabled):
|
port_security_enabled, security_profiles):
|
||||||
lport_obj['allowed_address_pairs'] = []
|
lport_obj['allowed_address_pairs'] = []
|
||||||
if port_security_enabled:
|
if port_security_enabled:
|
||||||
for fixed_ip in fixed_ips:
|
for fixed_ip in fixed_ips:
|
||||||
@ -430,11 +430,13 @@ def _configure_extensions(lport_obj, mac_address, fixed_ips,
|
|||||||
lport_obj["allowed_address_pairs"].append(
|
lport_obj["allowed_address_pairs"].append(
|
||||||
{"mac_address": mac_address,
|
{"mac_address": mac_address,
|
||||||
"ip_address": "0.0.0.0"})
|
"ip_address": "0.0.0.0"})
|
||||||
|
lport_obj['security_profiles'] = list(security_profiles or [])
|
||||||
|
|
||||||
|
|
||||||
def update_port(cluster, lswitch_uuid, lport_uuid, quantum_port_id, tenant_id,
|
def update_port(cluster, lswitch_uuid, lport_uuid, quantum_port_id, tenant_id,
|
||||||
display_name, device_id, admin_status_enabled,
|
display_name, device_id, admin_status_enabled,
|
||||||
mac_address=None, fixed_ips=None, port_security_enabled=None):
|
mac_address=None, fixed_ips=None, port_security_enabled=None,
|
||||||
|
security_profiles=None):
|
||||||
|
|
||||||
# device_id can be longer than 40 so we rehash it
|
# device_id can be longer than 40 so we rehash it
|
||||||
hashed_device_id = hashlib.sha1(device_id).hexdigest()
|
hashed_device_id = hashlib.sha1(device_id).hexdigest()
|
||||||
@ -446,7 +448,7 @@ def update_port(cluster, lswitch_uuid, lport_uuid, quantum_port_id, tenant_id,
|
|||||||
dict(scope='vm_id', tag=hashed_device_id)])
|
dict(scope='vm_id', tag=hashed_device_id)])
|
||||||
|
|
||||||
_configure_extensions(lport_obj, mac_address, fixed_ips,
|
_configure_extensions(lport_obj, mac_address, fixed_ips,
|
||||||
port_security_enabled)
|
port_security_enabled, security_profiles)
|
||||||
|
|
||||||
path = "/ws.v1/lswitch/" + lswitch_uuid + "/lport/" + lport_uuid
|
path = "/ws.v1/lswitch/" + lswitch_uuid + "/lport/" + lport_uuid
|
||||||
try:
|
try:
|
||||||
@ -465,7 +467,8 @@ def update_port(cluster, lswitch_uuid, lport_uuid, quantum_port_id, tenant_id,
|
|||||||
|
|
||||||
def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
|
def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
|
||||||
display_name, device_id, admin_status_enabled,
|
display_name, device_id, admin_status_enabled,
|
||||||
mac_address=None, fixed_ips=None, port_security_enabled=None):
|
mac_address=None, fixed_ips=None, port_security_enabled=None,
|
||||||
|
security_profiles=None):
|
||||||
""" Creates a logical port on the assigned logical switch """
|
""" Creates a logical port on the assigned logical switch """
|
||||||
# device_id can be longer than 40 so we rehash it
|
# device_id can be longer than 40 so we rehash it
|
||||||
hashed_device_id = hashlib.sha1(device_id).hexdigest()
|
hashed_device_id = hashlib.sha1(device_id).hexdigest()
|
||||||
@ -478,7 +481,7 @@ def create_lport(cluster, lswitch_uuid, tenant_id, quantum_port_id,
|
|||||||
)
|
)
|
||||||
|
|
||||||
_configure_extensions(lport_obj, mac_address, fixed_ips,
|
_configure_extensions(lport_obj, mac_address, fixed_ips,
|
||||||
port_security_enabled)
|
port_security_enabled, security_profiles)
|
||||||
|
|
||||||
path = _build_uri_path(LPORT_RESOURCE, parent_resource_id=lswitch_uuid)
|
path = _build_uri_path(LPORT_RESOURCE, parent_resource_id=lswitch_uuid)
|
||||||
try:
|
try:
|
||||||
@ -538,3 +541,148 @@ def plug_interface(cluster, lswitch_id, port, type, attachment=None):
|
|||||||
|
|
||||||
result = json.dumps(resp_obj)
|
result = json.dumps(resp_obj)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
# Security Profile convenience functions.
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
EXT_SECURITY_PROFILE_ID_SCOPE = 'nova_spid'
|
||||||
|
TENANT_ID_SCOPE = 'os_tid'
|
||||||
|
|
||||||
|
|
||||||
|
def format_exception(etype, e, execption_locals, request=None):
|
||||||
|
"""Consistent formatting for exceptions.
|
||||||
|
:param etype: a string describing the exception type.
|
||||||
|
:param e: the exception.
|
||||||
|
:param request: the request object.
|
||||||
|
:param execption_locals: calling context local variable dict.
|
||||||
|
:returns: a formatted string.
|
||||||
|
"""
|
||||||
|
msg = ["Error. %s exception: %s." % (etype, e)]
|
||||||
|
if request:
|
||||||
|
msg.append("request=[%s]" % request)
|
||||||
|
if request.body:
|
||||||
|
msg.append("request.body=[%s]" % str(request.body))
|
||||||
|
l = dict((k, v) for k, v in execption_locals if k != 'request')
|
||||||
|
msg.append("locals=[%s]" % str(l))
|
||||||
|
return ' '.join(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def do_request(*args, **kwargs):
|
||||||
|
"""Convenience function wraps do_single_request.
|
||||||
|
|
||||||
|
:param args: a list of positional arguments.
|
||||||
|
:param kwargs: a list of keyworkds arguments.
|
||||||
|
:returns: the result of do_single_request loaded into a python object
|
||||||
|
or None."""
|
||||||
|
res = do_single_request(*args, **kwargs)
|
||||||
|
if res:
|
||||||
|
return json.loads(res)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def mk_body(**kwargs):
|
||||||
|
"""Convenience function creates and dumps dictionary to string.
|
||||||
|
|
||||||
|
:param kwargs: the key/value pirs to be dumped into a json string.
|
||||||
|
:returns: a json string."""
|
||||||
|
return json.dumps(kwargs, ensure_ascii=False)
|
||||||
|
|
||||||
|
|
||||||
|
def set_tenant_id_tag(tenant_id, taglist=None):
|
||||||
|
"""Convenience function to add tenant_id tag to taglist.
|
||||||
|
|
||||||
|
:param tenant_id: the tenant_id to set.
|
||||||
|
:param taglist: the taglist to append to (or None).
|
||||||
|
:returns: a new taglist that includes the old taglist with the new
|
||||||
|
tenant_id tag set."""
|
||||||
|
new_taglist = []
|
||||||
|
if taglist:
|
||||||
|
new_taglist = [x for x in taglist if x['scope'] != TENANT_ID_SCOPE]
|
||||||
|
new_taglist.append(dict(scope=TENANT_ID_SCOPE, tag=tenant_id))
|
||||||
|
return new_taglist
|
||||||
|
|
||||||
|
|
||||||
|
def set_ext_security_profile_id_tag(external_id, taglist=None):
|
||||||
|
"""Convenience function to add spid tag to taglist.
|
||||||
|
|
||||||
|
:param external_id: the security_profile id from nova
|
||||||
|
:param taglist: the taglist to append to (or None).
|
||||||
|
:returns: a new taglist that includes the old taglist with the new
|
||||||
|
spid tag set."""
|
||||||
|
new_taglist = []
|
||||||
|
if taglist:
|
||||||
|
new_taglist = [x for x in taglist if x['scope'] !=
|
||||||
|
EXT_SECURITY_PROFILE_ID_SCOPE]
|
||||||
|
if external_id:
|
||||||
|
new_taglist.append(dict(scope=EXT_SECURITY_PROFILE_ID_SCOPE,
|
||||||
|
tag=str(external_id)))
|
||||||
|
return new_taglist
|
||||||
|
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Security Group API Calls
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
def create_security_profile(cluster, tenant_id, security_profile):
|
||||||
|
path = "/ws.v1/security-profile"
|
||||||
|
tags = set_tenant_id_tag(tenant_id)
|
||||||
|
tags = set_ext_security_profile_id_tag(
|
||||||
|
security_profile.get('external_id'), tags)
|
||||||
|
# Allow all dhcp responses in
|
||||||
|
dhcp = {'logical_port_egress_rules': [{'ethertype': 'IPv4',
|
||||||
|
'protocol': 17,
|
||||||
|
'port_range_min': 68,
|
||||||
|
'port_range_max': 68,
|
||||||
|
'ip_prefix': '0.0.0.0/0'}],
|
||||||
|
'logical_port_ingress_rules': []}
|
||||||
|
try:
|
||||||
|
body = mk_body(
|
||||||
|
tags=tags, display_name=security_profile.get('name'),
|
||||||
|
logical_port_ingress_rules=dhcp['logical_port_ingress_rules'],
|
||||||
|
logical_port_egress_rules=dhcp['logical_port_egress_rules'])
|
||||||
|
rsp = do_request("POST", path, body, cluster=cluster)
|
||||||
|
except NvpApiClient.NvpApiException as e:
|
||||||
|
LOG.error(format_exception("Unknown", e, locals()))
|
||||||
|
raise exception.QuantumException()
|
||||||
|
if security_profile.get('name') == 'default':
|
||||||
|
# If security group is default allow ip traffic between
|
||||||
|
# members of the same security profile.
|
||||||
|
rules = {'logical_port_egress_rules': [{'ethertype': 'IPv4',
|
||||||
|
'profile_uuid': rsp['uuid']},
|
||||||
|
{'ethertype': 'IPv6',
|
||||||
|
'profile_uuid': rsp['uuid']}],
|
||||||
|
'logical_port_ingress_rules': []}
|
||||||
|
|
||||||
|
update_security_group_rules(cluster, rsp['uuid'], rules)
|
||||||
|
LOG.debug("Created Security Profile: %s" % rsp)
|
||||||
|
return rsp
|
||||||
|
|
||||||
|
|
||||||
|
def update_security_group_rules(cluster, spid, rules):
|
||||||
|
path = "/ws.v1/security-profile/%s" % spid
|
||||||
|
|
||||||
|
# Allow all dhcp responses in
|
||||||
|
rules['logical_port_egress_rules'].append(
|
||||||
|
{'ethertype': 'IPv4', 'protocol': constants.UDP_PROTOCOL,
|
||||||
|
'port_range_min': constants.DHCP_RESPONSE_PORT,
|
||||||
|
'port_range_max': constants.DHCP_RESPONSE_PORT,
|
||||||
|
'ip_prefix': '0.0.0.0/0'})
|
||||||
|
try:
|
||||||
|
body = mk_body(
|
||||||
|
logical_port_ingress_rules=rules['logical_port_ingress_rules'],
|
||||||
|
logical_port_egress_rules=rules['logical_port_egress_rules'])
|
||||||
|
rsp = do_request("PUT", path, body, cluster=cluster)
|
||||||
|
except NvpApiClient.NvpApiException as e:
|
||||||
|
LOG.error(format_exception("Unknown", e, locals()))
|
||||||
|
raise exception.QuantumException()
|
||||||
|
LOG.debug("Updated Security Profile: %s" % rsp)
|
||||||
|
return rsp
|
||||||
|
|
||||||
|
|
||||||
|
def delete_security_profile(cluster, spid):
|
||||||
|
path = "/ws.v1/security-profile/%s" % spid
|
||||||
|
|
||||||
|
try:
|
||||||
|
do_request("DELETE", path, cluster=cluster)
|
||||||
|
except NvpApiClient.NvpApiException as e:
|
||||||
|
LOG.error(format_exception("Unknown", e, locals()))
|
||||||
|
raise exception.QuantumException()
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"display_name": "%(display_name)s",
|
||||||
|
"_href": "/ws.v1/security-profile/%(uuid)s",
|
||||||
|
"tags": [{"scope": "os_tid", "tag": "%(tenant_id)s"},
|
||||||
|
{"scope": "nova_spid", "tag": "%(nova_spid)s"}],
|
||||||
|
"logical_port_egress_rules": [],
|
||||||
|
"_schema": "/ws.v1/schema/SecurityProfileConfig",
|
||||||
|
"logical_port_ingress_rules": [],
|
||||||
|
"uuid": "%(uuid)s"
|
||||||
|
}
|
@ -35,17 +35,20 @@ class FakeClient:
|
|||||||
|
|
||||||
FAKE_POST_RESPONSES = {
|
FAKE_POST_RESPONSES = {
|
||||||
"lswitch": "fake_post_lswitch.json",
|
"lswitch": "fake_post_lswitch.json",
|
||||||
"lport": "fake_post_lport.json"
|
"lport": "fake_post_lport.json",
|
||||||
|
"securityprofile": "fake_post_security_profile.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
FAKE_PUT_RESPONSES = {
|
FAKE_PUT_RESPONSES = {
|
||||||
"lswitch": "fake_post_lswitch.json",
|
"lswitch": "fake_post_lswitch.json",
|
||||||
"lport": "fake_post_lport.json"
|
"lport": "fake_post_lport.json",
|
||||||
|
"securityprofile": "fake_post_security_profile.json"
|
||||||
}
|
}
|
||||||
|
|
||||||
_fake_lswitch_dict = {}
|
_fake_lswitch_dict = {}
|
||||||
_fake_lport_dict = {}
|
_fake_lport_dict = {}
|
||||||
_fake_lportstatus_dict = {}
|
_fake_lportstatus_dict = {}
|
||||||
|
_fake_securityprofile_dict = {}
|
||||||
|
|
||||||
def __init__(self, fake_files_path):
|
def __init__(self, fake_files_path):
|
||||||
self.fake_files_path = fake_files_path
|
self.fake_files_path = fake_files_path
|
||||||
@ -102,17 +105,32 @@ class FakeClient:
|
|||||||
self._fake_lportstatus_dict[fake_lport['uuid']] = fake_lport_status
|
self._fake_lportstatus_dict[fake_lport['uuid']] = fake_lport_status
|
||||||
return fake_lport
|
return fake_lport
|
||||||
|
|
||||||
|
def _add_securityprofile(self, body):
|
||||||
|
fake_securityprofile = json.loads(body)
|
||||||
|
fake_securityprofile['uuid'] = uuidutils.generate_uuid()
|
||||||
|
fake_securityprofile['tenant_id'] = self._get_tag(
|
||||||
|
fake_securityprofile, 'os_tid')
|
||||||
|
|
||||||
|
fake_securityprofile['nova_spid'] = self._get_tag(fake_securityprofile,
|
||||||
|
'nova_spid')
|
||||||
|
self._fake_securityprofile_dict[fake_securityprofile['uuid']] = (
|
||||||
|
fake_securityprofile)
|
||||||
|
return fake_securityprofile
|
||||||
|
|
||||||
def _get_resource_type(self, path):
|
def _get_resource_type(self, path):
|
||||||
uri_split = path.split('/')
|
uri_split = path.split('/')
|
||||||
resource_type = ('status' in uri_split and
|
resource_type = ('status' in uri_split and
|
||||||
'lport' in uri_split and 'lportstatus'
|
'lport' in uri_split and 'lportstatus'
|
||||||
or 'lport' in uri_split and 'lport'
|
or 'lport' in uri_split and 'lport'
|
||||||
or 'lswitch' in uri_split and 'lswitch')
|
or 'lswitch' in uri_split and 'lswitch' or
|
||||||
|
'security-profile' in uri_split and 'securityprofile')
|
||||||
switch_uuid = ('lswitch' in uri_split and
|
switch_uuid = ('lswitch' in uri_split and
|
||||||
len(uri_split) > 3 and uri_split[3])
|
len(uri_split) > 3 and uri_split[3])
|
||||||
port_uuid = ('lport' in uri_split and
|
port_uuid = ('lport' in uri_split and
|
||||||
len(uri_split) > 5 and uri_split[5])
|
len(uri_split) > 5 and uri_split[5])
|
||||||
return (resource_type, switch_uuid, port_uuid)
|
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)
|
||||||
|
|
||||||
def _list(self, resource_type, response_file,
|
def _list(self, resource_type, response_file,
|
||||||
switch_uuid=None, query=None):
|
switch_uuid=None, query=None):
|
||||||
@ -176,7 +194,8 @@ class FakeClient:
|
|||||||
def handle_get(self, url):
|
def handle_get(self, url):
|
||||||
#TODO(salvatore-orlando): handle field selection
|
#TODO(salvatore-orlando): handle field selection
|
||||||
parsedurl = urlparse.urlparse(url)
|
parsedurl = urlparse.urlparse(url)
|
||||||
(res_type, s_uuid, p_uuid) = self._get_resource_type(parsedurl.path)
|
(res_type, s_uuid, p_uuid, sec_uuid) = self._get_resource_type(
|
||||||
|
parsedurl.path)
|
||||||
response_file = self.FAKE_GET_RESPONSES.get(res_type)
|
response_file = self.FAKE_GET_RESPONSES.get(res_type)
|
||||||
if not response_file:
|
if not response_file:
|
||||||
raise Exception("resource not found")
|
raise Exception("resource not found")
|
||||||
@ -199,7 +218,8 @@ class FakeClient:
|
|||||||
|
|
||||||
def handle_post(self, url, body):
|
def handle_post(self, url, body):
|
||||||
parsedurl = urlparse.urlparse(url)
|
parsedurl = urlparse.urlparse(url)
|
||||||
(res_type, s_uuid, _p) = self._get_resource_type(parsedurl.path)
|
(res_type, s_uuid, _p, sec_uuid) = self._get_resource_type(
|
||||||
|
parsedurl.path)
|
||||||
response_file = self.FAKE_POST_RESPONSES.get(res_type)
|
response_file = self.FAKE_POST_RESPONSES.get(res_type)
|
||||||
if not response_file:
|
if not response_file:
|
||||||
raise Exception("resource not found")
|
raise Exception("resource not found")
|
||||||
@ -214,8 +234,9 @@ class FakeClient:
|
|||||||
|
|
||||||
def handle_put(self, url, body):
|
def handle_put(self, url, body):
|
||||||
parsedurl = urlparse.urlparse(url)
|
parsedurl = urlparse.urlparse(url)
|
||||||
(res_type, s_uuid, p_uuid) = self._get_resource_type(parsedurl.path)
|
(res_type, s_uuid, p_uuid, sec_uuid) = self._get_resource_type(
|
||||||
target_uuid = p_uuid or s_uuid
|
parsedurl.path)
|
||||||
|
target_uuid = p_uuid or s_uuid or sec_uuid
|
||||||
response_file = self.FAKE_PUT_RESPONSES.get(res_type)
|
response_file = self.FAKE_PUT_RESPONSES.get(res_type)
|
||||||
if not response_file:
|
if not response_file:
|
||||||
raise Exception("resource not found")
|
raise Exception("resource not found")
|
||||||
@ -229,8 +250,9 @@ class FakeClient:
|
|||||||
|
|
||||||
def handle_delete(self, url):
|
def handle_delete(self, url):
|
||||||
parsedurl = urlparse.urlparse(url)
|
parsedurl = urlparse.urlparse(url)
|
||||||
(res_type, s_uuid, p_uuid) = self._get_resource_type(parsedurl.path)
|
(res_type, s_uuid, p_uuid, sec_uuid) = self._get_resource_type(
|
||||||
target_uuid = p_uuid or s_uuid
|
parsedurl.path)
|
||||||
|
target_uuid = p_uuid or s_uuid or sec_uuid
|
||||||
response_file = self.FAKE_PUT_RESPONSES.get(res_type)
|
response_file = self.FAKE_PUT_RESPONSES.get(res_type)
|
||||||
if not response_file:
|
if not response_file:
|
||||||
raise Exception("resource not found")
|
raise Exception("resource not found")
|
||||||
|
@ -22,13 +22,14 @@ import webob.exc
|
|||||||
import quantum.common.test_lib as test_lib
|
import quantum.common.test_lib as test_lib
|
||||||
from quantum import context
|
from quantum import context
|
||||||
from quantum.extensions import providernet as pnet
|
from quantum.extensions import providernet as pnet
|
||||||
|
from quantum.extensions import securitygroup as secgrp
|
||||||
from quantum import manager
|
from quantum import manager
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
from quantum.plugins.nicira.nicira_nvp_plugin import nvplib
|
from quantum.plugins.nicira.nicira_nvp_plugin import nvplib
|
||||||
from quantum.tests.unit.nicira import fake_nvpapiclient
|
from quantum.tests.unit.nicira import fake_nvpapiclient
|
||||||
import quantum.tests.unit.test_db_plugin as test_plugin
|
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_portsecurity as psec
|
||||||
|
import quantum.tests.unit.test_extension_security_group as ext_sg
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
NICIRA_PKG_PATH = 'quantum.plugins.nicira.nicira_nvp_plugin'
|
NICIRA_PKG_PATH = 'quantum.plugins.nicira.nicira_nvp_plugin'
|
||||||
@ -120,6 +121,24 @@ class TestNiciraPortsV2(test_plugin.TestPortsV2, NiciraPluginV2TestCase):
|
|||||||
net['network']['id'])
|
net['network']['id'])
|
||||||
self.assertEqual(len(ls), 2)
|
self.assertEqual(len(ls), 2)
|
||||||
|
|
||||||
|
def test_update_port_delete_ip(self):
|
||||||
|
# This test case overrides the default because the nvp plugin
|
||||||
|
# implements port_security/security groups and it is not allowed
|
||||||
|
# to remove an ip address from a port unless the security group
|
||||||
|
# is first removed.
|
||||||
|
with self.subnet() as subnet:
|
||||||
|
with self.port(subnet=subnet) as port:
|
||||||
|
data = {'port': {'admin_state_up': False,
|
||||||
|
'fixed_ips': [],
|
||||||
|
secgrp.SECURITYGROUPS: []}}
|
||||||
|
req = self.new_update_request('ports',
|
||||||
|
data, port['port']['id'])
|
||||||
|
res = self.deserialize('json', req.get_response(self.api))
|
||||||
|
self.assertEqual(res['port']['admin_state_up'],
|
||||||
|
data['port']['admin_state_up'])
|
||||||
|
self.assertEqual(res['port']['fixed_ips'],
|
||||||
|
data['port']['fixed_ips'])
|
||||||
|
|
||||||
|
|
||||||
class TestNiciraNetworksV2(test_plugin.TestNetworksV2,
|
class TestNiciraNetworksV2(test_plugin.TestNetworksV2,
|
||||||
NiciraPluginV2TestCase):
|
NiciraPluginV2TestCase):
|
||||||
@ -185,3 +204,34 @@ class NiciraPortSecurityTestCase(psec.PortSecurityDBTestCase):
|
|||||||
class TestNiciraPortSecurity(psec.TestPortSecurity,
|
class TestNiciraPortSecurity(psec.TestPortSecurity,
|
||||||
NiciraPortSecurityTestCase):
|
NiciraPortSecurityTestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NiciraSecurityGroupsTestCase(ext_sg.SecurityGroupDBTestCase):
|
||||||
|
|
||||||
|
_plugin_name = ('%s.QuantumPlugin.NvpPluginV2' % NICIRA_PKG_PATH)
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
etc_path = os.path.join(os.path.dirname(__file__), 'etc')
|
||||||
|
test_lib.test_config['config_files'] = [os.path.join(etc_path,
|
||||||
|
'nvp.ini.test')]
|
||||||
|
# mock nvp api client
|
||||||
|
fc = fake_nvpapiclient.FakeClient(etc_path)
|
||||||
|
self.mock_nvpapi = mock.patch('%s.NvpApiClient.NVPApiHelper'
|
||||||
|
% NICIRA_PKG_PATH, autospec=True)
|
||||||
|
instance = self.mock_nvpapi.start()
|
||||||
|
instance.return_value.login.return_value = "the_cookie"
|
||||||
|
|
||||||
|
def _fake_request(*args, **kwargs):
|
||||||
|
return fc.fake_request(*args, **kwargs)
|
||||||
|
|
||||||
|
instance.return_value.request.side_effect = _fake_request
|
||||||
|
super(NiciraSecurityGroupsTestCase, self).setUp(self._plugin_name)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
super(NiciraSecurityGroupsTestCase, self).tearDown()
|
||||||
|
self.mock_nvpapi.stop()
|
||||||
|
|
||||||
|
|
||||||
|
class TestNiciraSecurityGroup(ext_sg.TestSecurityGroups,
|
||||||
|
NiciraSecurityGroupsTestCase):
|
||||||
|
pass
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Copyright (c) 2012 OpenStack, LLC.
|
# Copyright (c) 2012 OpenStack, LLC.
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
# you may not use this file except in compliance with the License.
|
# you may not use this file except in compliance with the License.
|
||||||
# You may obtain a copy of the License at
|
# You may obtain a copy of the License at
|
||||||
@ -19,6 +19,7 @@ import os
|
|||||||
import mock
|
import mock
|
||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
|
from quantum.api.v2 import attributes as attr
|
||||||
from quantum.common.test_lib import test_config
|
from quantum.common.test_lib import test_config
|
||||||
from quantum import context
|
from quantum import context
|
||||||
from quantum.db import db_base_plugin_v2
|
from quantum.db import db_base_plugin_v2
|
||||||
@ -174,7 +175,7 @@ class SecurityGroupTestPlugin(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
def create_port(self, context, port):
|
def create_port(self, context, port):
|
||||||
tenant_id = self._get_tenant_id_for_create(context, port['port'])
|
tenant_id = self._get_tenant_id_for_create(context, port['port'])
|
||||||
default_sg = self._ensure_default_security_group(context, tenant_id)
|
default_sg = self._ensure_default_security_group(context, tenant_id)
|
||||||
if not port['port'].get(ext_sg.SECURITYGROUPS):
|
if not attr.is_attr_set(port['port'].get(ext_sg.SECURITYGROUPS)):
|
||||||
port['port'][ext_sg.SECURITYGROUPS] = [default_sg]
|
port['port'][ext_sg.SECURITYGROUPS] = [default_sg]
|
||||||
session = context.session
|
session = context.session
|
||||||
with session.begin(subtransactions=True):
|
with session.begin(subtransactions=True):
|
||||||
@ -207,6 +208,13 @@ class SecurityGroupTestPlugin(db_base_plugin_v2.QuantumDbPluginV2,
|
|||||||
return super(SecurityGroupTestPlugin, self).create_network(context,
|
return super(SecurityGroupTestPlugin, self).create_network(context,
|
||||||
network)
|
network)
|
||||||
|
|
||||||
|
def get_ports(self, context, filters=None, fields=None):
|
||||||
|
quantum_lports = super(SecurityGroupTestPlugin, self).get_ports(
|
||||||
|
context, filters)
|
||||||
|
for quantum_lport in quantum_lports:
|
||||||
|
self._extend_port_dict_security_group(context, quantum_lport)
|
||||||
|
return quantum_lports
|
||||||
|
|
||||||
|
|
||||||
class SecurityGroupDBTestCase(SecurityGroupsTestCase):
|
class SecurityGroupDBTestCase(SecurityGroupsTestCase):
|
||||||
def setUp(self, plugin=None):
|
def setUp(self, plugin=None):
|
||||||
@ -215,6 +223,10 @@ class SecurityGroupDBTestCase(SecurityGroupsTestCase):
|
|||||||
test_config['extension_manager'] = ext_mgr
|
test_config['extension_manager'] = ext_mgr
|
||||||
super(SecurityGroupDBTestCase, self).setUp(plugin)
|
super(SecurityGroupDBTestCase, self).setUp(plugin)
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
del test_config['plugin_name_v2']
|
||||||
|
super(SecurityGroupDBTestCase, self).tearDown()
|
||||||
|
|
||||||
|
|
||||||
class TestSecurityGroups(SecurityGroupDBTestCase):
|
class TestSecurityGroups(SecurityGroupDBTestCase):
|
||||||
def test_create_security_group(self):
|
def test_create_security_group(self):
|
||||||
@ -649,6 +661,18 @@ class TestSecurityGroups(SecurityGroupDBTestCase):
|
|||||||
self.deserialize(self.fmt, res)
|
self.deserialize(self.fmt, res)
|
||||||
self.assertEqual(res.status_int, 400)
|
self.assertEqual(res.status_int, 400)
|
||||||
|
|
||||||
|
def test_list_ports_security_group(self):
|
||||||
|
with self.network() as n:
|
||||||
|
with self.subnet(n):
|
||||||
|
res = self._create_port(self.fmt, n['network']['id'])
|
||||||
|
self.deserialize(self.fmt, res)
|
||||||
|
res = self.new_list_request('ports')
|
||||||
|
ports = self.deserialize(self.fmt,
|
||||||
|
res.get_response(self.api))
|
||||||
|
port = ports['ports'][0]
|
||||||
|
self.assertEquals(len(port[ext_sg.SECURITYGROUPS]), 1)
|
||||||
|
self._delete('ports', port['id'])
|
||||||
|
|
||||||
def test_update_port_with_security_group(self):
|
def test_update_port_with_security_group(self):
|
||||||
with self.network() as n:
|
with self.network() as n:
|
||||||
with self.subnet(n):
|
with self.subnet(n):
|
||||||
|
Loading…
Reference in New Issue
Block a user