'type:uuid_list': _validate_uuid_list,
'type:values': _validate_values}
+# Define constants for base resource name
+NETWORK = 'network'
+NETWORKS = '%ss' % NETWORK
+PORT = 'port'
+PORTS = '%ss' % PORT
+SUBNET = 'subnet'
+SUBNETS = '%ss' % SUBNET
# Note: a default of ATTR_NOT_SPECIFIED indicates that an
# attribute is not required, but will be generated by the plugin
# if it is not specified. Particularly, a value of ATTR_NOT_SPECIFIED
# mechanism, ie: there might be rules which refer to this attribute.
RESOURCE_ATTRIBUTE_MAP = {
- 'networks': {
+ NETWORKS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'required_by_policy': True,
'enforce_policy': True},
},
- 'ports': {
+ PORTS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
'status': {'allow_post': False, 'allow_put': False,
'is_visible': True},
},
- 'subnets': {
+ SUBNETS: {
'id': {'allow_post': False, 'allow_put': False,
'validate': {'type:uuid': None},
'is_visible': True,
# Resources without parents, such as networks, are not in this list
RESOURCE_HIERARCHY_MAP = {
- 'ports': {'parent': 'networks', 'identified_by': 'network_id'},
- 'subnets': {'parent': 'networks', 'identified_by': 'network_id'}
+ PORTS: {'parent': NETWORKS, 'identified_by': 'network_id'},
+ SUBNETS: {'parent': NETWORKS, 'identified_by': 'network_id'}
}
-PLURALS = {'networks': 'network',
- 'ports': 'port',
- 'subnets': 'subnet',
+PLURALS = {NETWORKS: NETWORK,
+ PORTS: PORT,
+ SUBNETS: SUBNET,
'dns_nameservers': 'dns_nameserver',
'host_routes': 'host_route',
'allocation_pools': 'allocation_pool',
# To this aim, the register_model_query_hook and unregister_query_hook
# from this class should be invoked
_model_query_hooks = {}
+ # This dictionary will store methods for extending attributes of
+ # api resources. Mixins can use this dict for adding their own methods
+ # TODO(salvatore-orlando): Avoid using class-level variables
+ _dict_extend_functions = {}
def __init__(self):
# NOTE(jkoelker) This is an incomlete implementation. Subclasses
query = query.filter(query_filter)
return query
+ @classmethod
+ def register_dict_extend_funcs(cls, resource, funcs):
+ cur_funcs = cls._dict_extend_functions.get(resource, [])
+ cur_funcs.extend(funcs)
+ cls._dict_extend_functions[resource] = cur_funcs
+
@classmethod
def register_model_query_hook(cls, model, name, query_hook, filter_hook,
result_filters=None):
model_hooks[name] = {'query': query_hook, 'filter': filter_hook,
'result_filters': result_filters}
+ def _filter_non_model_columns(self, data, model):
+ """Remove all the attributes from data which are not columns of
+ the model passed as second parameter.
+ """
+ columns = [c.name for c in model.__table__.columns]
+ return dict((k, v) for (k, v) in
+ data.iteritems() if k in columns)
+
def _get_by_id(self, context, model, id):
query = self._model_query(context, model)
return query.filter(model.id == id).one()
}
return self._fields(res, fields)
- def _make_port_dict(self, port, fields=None):
+ def _make_port_dict(self, port, fields=None,
+ process_extensions=True):
res = {"id": port["id"],
'name': port['name'],
"network_id": port["network_id"],
for ip in port["fixed_ips"]],
"device_id": port["device_id"],
"device_owner": port["device_owner"]}
+ # Call auxiliary extend functions, if any
+ if process_extensions:
+ for func in self._dict_extend_functions.get(attributes.PORTS,
+ []):
+ func(self, res, port)
return self._fields(res, fields)
def _create_bulk(self, resource, context, request_items):
)
context.session.add(allocated)
- return self._make_port_dict(port)
+ return self._make_port_dict(port, process_extensions=False)
def update_port(self, context, id, port):
p = port['port']
if 'fixed_ips' in p:
self._recycle_expired_ip_allocations(context,
port['network_id'])
- original = self._make_port_dict(port)
+ original = self._make_port_dict(port, process_extensions=False)
ips = self._update_ips_for_port(context,
port["network_id"],
id,
original["fixed_ips"],
p['fixed_ips'])
- # 'fixed_ip's not part of DB so it is deleted
- del p['fixed_ips']
-
# Update ips if necessary
for ip in ips:
allocated = models_v2.IPAllocation(
ip_address=ip['ip_address'], subnet_id=ip['subnet_id'],
expiration=self._default_allocation_expiration())
context.session.add(allocated)
-
- port.update(p)
+ # Remove all attributes in p which are not in the port DB model
+ # and then update the port
+ port.update(self._filter_non_model_columns(p, models_v2.Port))
return self._make_port_dict(port)
from sqlalchemy.orm import scoped_session
from quantum.api.v2 import attributes as attr
+from quantum.db import db_base_plugin_v2
from quantum.db import model_base
from quantum.db import models_v2
from quantum.extensions import securitygroup as ext_sg
sa.ForeignKey("securitygroups.id"),
primary_key=True)
+ # Add a relationship to the Port model in order to instruct SQLAlchemy to
+ # eagerly load security group bindings
+ ports = orm.relationship(
+ models_v2.Port,
+ backref=orm.backref("security_groups",
+ lazy='joined', cascade='delete'))
+
class SecurityGroupRule(model_base.BASEV2, models_v2.HasId,
models_v2.HasTenant):
rule = self._get_security_group_rule(context, id)
context.session.delete(rule)
- def _extend_port_dict_security_group(self, context, port):
- filters = {'port_id': [port['id']]}
- fields = {'security_group_id': None}
- security_group_id = self._get_port_security_group_bindings(
- context, filters, fields)
-
- port[ext_sg.SECURITYGROUPS] = []
- for security_group_id in security_group_id:
- port[ext_sg.SECURITYGROUPS].append(
- security_group_id['security_group_id'])
- return port
-
- def _process_port_create_security_group(self, context, port_id,
- security_group_id):
- if not attr.is_attr_set(security_group_id):
- return
- for security_group_id in security_group_id:
- self._create_port_security_group_binding(context, port_id,
- security_group_id)
+ def _extend_port_dict_security_group(self, port_res, port_db):
+ # If port_db is provided, security groups will be accessed via
+ # sqlalchemy models. As they're loaded together with ports this
+ # will not cause an extra query.
+ security_group_ids = [sec_group_mapping['security_group_id'] for
+ sec_group_mapping in port_db.security_groups]
+ port_res[ext_sg.SECURITYGROUPS] = security_group_ids
+ return port_res
+
+ # Register dict extend functions for ports
+ db_base_plugin_v2.QuantumDbPluginV2.register_dict_extend_funcs(
+ attr.PORTS, [_extend_port_dict_security_group])
+
+ def _process_port_create_security_group(self, context, port,
+ security_group_ids):
+ if attr.is_attr_set(security_group_ids):
+ for security_group_id in security_group_ids:
+ self._create_port_security_group_binding(context, port['id'],
+ security_group_id)
+ # Convert to list as a set might be passed here and
+ # this has to be serialized
+ port[ext_sg.SECURITYGROUPS] = (security_group_ids and
+ list(security_group_ids) or [])
def _ensure_default_security_group(self, context, tenant_id):
"""Create a default security group if one doesn't exist.
self._delete_port_security_group_bindings(context, id)
self._process_port_create_security_group(
context,
- id,
+ updated_port,
port['port'][ext_sg.SECURITYGROUPS])
need_notify = True
- self._extend_port_dict_security_group(context, updated_port)
+ else:
+ updated_port[ext_sg.SECURITYGROUPS] = (
+ original_port[ext_sg.SECURITYGROUPS])
return need_notify
def is_security_group_member_updated(self, context,
port['port'][ext_sg.SECURITYGROUPS] = (
self._get_security_groups_on_port(context, port))
self._delete_port_security_group_bindings(context, port_id)
+ # process_port_create_security_group also needs port id
+ port['port']['id'] = port_id
self._process_port_create_security_group(
context,
- port_id,
+ port['port'],
port['port'][ext_sg.SECURITYGROUPS])
port_updated = True
port = super(BrocadePluginV2, self).update_port(
context, port_id, port)
- self._extend_port_dict_security_group(context, port)
if original_port['admin_state_up'] != port['admin_state_up']:
port_updated = True
with context.session.begin(subtransactions=True):
port = super(BrocadePluginV2, self).get_port(
context, port_id, fields)
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
return self._fields(port, fields)
filters,
fields)
for port in ports:
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
res_ports.append(self._fields(port, fields))
port = super(LinuxBridgePluginV2, self).get_port(context,
id,
fields)
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port),
return self._fields(port, fields)
limit, marker, page_reverse)
#TODO(nati) filter by security group
for port in ports:
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
res_ports.append(self._fields(port, fields))
return res_ports
port = super(LinuxBridgePluginV2,
self).create_port(context, port)
self._process_port_create_security_group(
- context, port['id'], sgids)
- self._extend_port_dict_security_group(context, port)
+ context, port, sgids)
self.notify_security_groups_member_updated(context, port)
return self._extend_port_dict_binding(context, port)
with session.begin(subtransactions=True):
port_db_entry = super(MidonetPluginV2,
self).create_port(context, port)
- self._extend_port_dict_security_group(context, port_db_entry)
+ # Caveat: port_db_entry is not a db model instance
+ sg_ids = self._get_security_groups_on_port(context, port)
+ self._process_port_create_security_group(context, port, sg_ids)
if is_compute_interface:
# Create a DHCP entry if needed.
if 'ip_address' in (port_db_entry['fixed_ips'] or [{}])[0]:
# get the quantum port from DB.
port_db_entry = super(MidonetPluginV2, self).get_port(context,
id, fields)
- self._extend_port_dict_security_group(context, port_db_entry)
-
# verify that corresponding port exists in MidoNet.
try:
self.mido_api.get_port(id)
try:
for port in ports_db_entry:
self.mido_api.get_port(port['id'])
- self._extend_port_dict_security_group(context, port)
except w_exc.HTTPNotFound:
raise MidonetResourceNotFound(resource_type='Port',
id=port['id'])
sgids = self._get_security_groups_on_port(context, port)
port = super(NECPluginV2, self).create_port(context, port)
self._process_port_create_security_group(
- context, port['id'], sgids)
- self._extend_port_dict_security_group(context, port)
+ context, port, sgids)
self.notify_security_groups_member_updated(context, port)
self._update_resource_status(context, "port", port['id'],
OperationalStatus.BUILD)
else:
self.deactivate_port(context, old_port)
- # NOTE: _extend_port_dict_security_group() is called in
- # update_security_group_on_port() above, so we don't need to
- # call it here.
return self._extend_port_dict_binding(context, new_port)
def delete_port(self, context, id, l3_port_check=True):
def get_port(self, context, id, fields=None):
with context.session.begin(subtransactions=True):
port = super(NECPluginV2, self).get_port(context, id, fields)
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
return self._fields(port, fields)
fields)
# TODO(amotoki) filter by security group
for port in ports:
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
return [self._fields(port, fields) for port in ports]
self.nvp_opts.nvp_gen_timeout)
db.configure_db()
+ # Extend the fault map
self._extend_fault_map()
# Set up RPC interface for DHCP agent
self.setup_rpc()
context, filters)
for quantum_lport in quantum_lports:
self._extend_port_port_security_dict(context, quantum_lport)
- self._extend_port_dict_security_group(context, quantum_lport)
if (filters.get('network_id') and len(filters.get('network_id')) and
self._network_is_external(context, filters['network_id'][0])):
# Do not perform check on NVP platform
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])
+ context, port_data, port_data[ext_sg.SECURITYGROUPS])
# QoS extension checks
port_data[ext_qos.QUEUE] = self._check_for_queue_and_create(
context, port_data)
# remove since it will be added in extend based on policy
del port_data[ext_qos.QUEUE]
self._extend_port_port_security_dict(context, port_data)
- self._extend_port_dict_security_group(context, port_data)
self._extend_port_qos_queue(context, port_data)
net = self.get_network(context, port_data['network_id'])
self.schedule_network(context, net)
with context.session.begin(subtransactions=True):
ret_port = super(NvpPluginV2, self).update_port(
context, id, port)
- # copy values over
+ # copy values over - except fixed_ips as
+ # they've alreaby been processed
+ port['port'].pop('fixed_ips', None)
ret_port.update(port['port'])
tenant_id = self._get_tenant_id_for_create(context, ret_port)
# populate port_security setting
# 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)
+ self._process_port_create_security_group(context, ret_port,
+ sgids)
if psec.PORTSECURITY in port['port']:
self._update_port_security_binding(
self._delete_port_queue_mapping(context, ret_port['id'])
self._process_port_queue_mapping(context, ret_port)
self._extend_port_port_security_dict(context, ret_port)
- self._extend_port_dict_security_group(context, ret_port)
- LOG.debug(_("Update port request: %s"), port)
+ LOG.warn(_("Update port request: %s"), port)
nvp_port_id = self._nvp_get_port_id(
context, self.cluster, ret_port)
nvplib.update_port(self.cluster,
quantum_db_port = super(NvpPluginV2, self).get_port(context,
id, fields)
self._extend_port_port_security_dict(context, quantum_db_port)
- self._extend_port_dict_security_group(context, quantum_db_port)
self._extend_port_qos_queue(context, quantum_db_port)
if self._network_is_external(context,
self._ensure_default_security_group_on_port(context, port)
sgids = self._get_security_groups_on_port(context, port)
port = super(OVSQuantumPluginV2, self).create_port(context, port)
- self._process_port_create_security_group(
- context, port['id'], sgids)
- self._extend_port_dict_security_group(context, port)
+ self._process_port_create_security_group(context, port, sgids)
self.notify_security_groups_member_updated(context, port)
return self._extend_port_dict_binding(context, port)
with context.session.begin(subtransactions=True):
port = super(OVSQuantumPluginV2, self).get_port(context,
id, fields)
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
return self._fields(port, fields)
page_reverse)
#TODO(nati) filter by security group
for port in ports:
- self._extend_port_dict_security_group(context, port)
self._extend_port_dict_binding(context, port)
return [self._fields(port, fields) for port in ports]
def update_port(self, context, id, port):
session = context.session
-
need_port_update_notify = False
with session.begin(subtransactions=True):
original_port = super(OVSQuantumPluginV2, self).get_port(
context, id, port)
need_port_update_notify = self.update_security_group_on_port(
context, id, port, original_port, updated_port)
-
need_port_update_notify |= self.is_security_group_member_updated(
context, original_port, updated_port)
-
if original_port['admin_state_up'] != updated_port['admin_state_up']:
need_port_update_notify = True
binding.network_type,
binding.segmentation_id,
binding.physical_network)
-
return self._extend_port_dict_binding(context, updated_port)
def delete_port(self, context, id, l3_port_check=True):
def __init__(self, configfile=None):
db.configure_db()
-
self.tunnel_key = db_api_v2.TunnelKey(
cfg.CONF.OVS.tunnel_key_min, cfg.CONF.OVS.tunnel_key_max)
self.ofp_api_host = cfg.CONF.OVS.openflow_rest_api
sgids = self._get_security_groups_on_port(context, port)
port = super(RyuQuantumPluginV2, self).create_port(context, port)
self._process_port_create_security_group(
- context, port['id'], sgids)
- self._extend_port_dict_security_group(context, port)
+ context, port, sgids)
self.notify_security_groups_member_updated(context, port)
self.iface_client.create_network_id(port['id'], port['network_id'])
return port
with context.session.begin(subtransactions=True):
port = super(RyuQuantumPluginV2, self).get_port(context, id,
fields)
- self._extend_port_dict_security_group(context, port)
return self._fields(port, fields)
def get_ports(self, context, filters=None, fields=None):
with context.session.begin(subtransactions=True):
ports = super(RyuQuantumPluginV2, self).get_ports(
context, filters, fields)
- for port in ports:
- self._extend_port_dict_security_group(context, port)
return [self._fields(port, fields) for port in ports]
if (p.get(ext_sg.SECURITYGROUPS) and p[psec.PORTSECURITY]):
self._process_port_create_security_group(
- context, p['id'], p[ext_sg.SECURITYGROUPS])
+ context, p, p[ext_sg.SECURITYGROUPS])
- self._extend_port_dict_security_group(context, p)
self._extend_port_port_security_dict(context, p)
return port['port']
with context.session.begin(subtransactions=True):
ret_port = super(PortSecurityTestPlugin, self).update_port(
context, id, port)
- # copy values over
+ # copy values over - but not fixed_ips
+ port['port'].pop('fixed_ips', None)
ret_port.update(port['port'])
# populate port_security setting
# 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)
+ # process port create sec groups needs port id
+ port['id'] = id
+ self._process_port_create_security_group(context,
+ ret_port, 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)
return ret_port
psec.PORTSECURITY: False}}
req = self.new_update_request('ports', update_port,
port['port']['id'])
-
port = self.deserialize('json', req.get_response(self.api))
self.assertEqual(port['port'][psec.PORTSECURITY], False)
self.assertEqual(len(port['port'][ext_sg.SECURITYGROUPS]), 0)
self._delete('ports', port['port']['id'])
- def test_update_port_remove_port_security_security_group_readd(self):
+ def test_update_port_remove_port_security_security_group_read(self):
if self._skip_security_group:
self.skipTest("Plugin does not support security groups")
with self.network() as net:
sgids = self._get_security_groups_on_port(context, port)
port = super(SecurityGroupTestPlugin, self).create_port(context,
port)
- self._process_port_create_security_group(context, port['id'],
+ self._process_port_create_security_group(context, port,
sgids)
- self._extend_port_dict_security_group(context, port)
return port
def update_port(self, context, id, port):
self._get_security_groups_on_port(context, port))
# delete the port binding and read it with the new rules
self._delete_port_security_group_bindings(context, id)
+ port['port']['id'] = id
self._process_port_create_security_group(
- context, id, port['port'].get(ext_sg.SECURITYGROUPS))
+ context, port['port'],
+ port['port'].get(ext_sg.SECURITYGROUPS))
port = super(SecurityGroupTestPlugin, self).update_port(
context, id, port)
- self._extend_port_dict_security_group(context, port)
return port
def create_network(self, context, network):
quantum_lports = super(SecurityGroupTestPlugin, self).get_ports(
context, filters, sorts=sorts, limit=limit, marker=marker,
page_reverse=page_reverse)
- for quantum_lport in quantum_lports:
- self._extend_port_dict_security_group(context, quantum_lport)
return quantum_lports
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))
+ self._create_port(self.fmt, n['network']['id'])
+ req = self.new_list_request('ports')
+ res = req.get_response(self.api)
+ ports = self.deserialize(self.fmt, res)
port = ports['ports'][0]
self.assertEqual(len(port[ext_sg.SECURITYGROUPS]), 1)
self._delete('ports', port['id'])