14
14
# under the License.
17
from oslo.config import cfg
18
from oslo.db import exception as db_exc
19
from oslo.utils import excutils
17
from oslo_config import cfg
18
from oslo_db import exception as db_exc
19
from oslo_log import log as logging
20
from oslo_utils import excutils
20
21
from sqlalchemy import and_
21
22
from sqlalchemy import event
22
23
from sqlalchemy import orm
35
36
from neutron.i18n import _LE, _LI
36
37
from neutron import manager
37
38
from neutron import neutron_plugin_base_v2
38
from neutron.openstack.common import log as logging
39
39
from neutron.openstack.common import uuidutils
40
40
from neutron.plugins.common import constants as service_constants
358
358
IPs. Include the subnet_id in the result if only an IP address is
361
:raises: InvalidInput, IpAddressInUse
361
:raises: InvalidInput, IpAddressInUse, InvalidIpForNetwork,
363
364
fixed_ip_set = []
364
365
for fixed in fixed_ips:
377
378
subnet_id = subnet['id']
380
msg = _('IP address %s is not a valid IP for the defined '
381
'networks subnets') % fixed['ip_address']
382
raise n_exc.InvalidInput(error_message=msg)
381
raise n_exc.InvalidIpForNetwork(
382
ip_address=fixed['ip_address'])
384
384
subnet = self._get_subnet(context, fixed['subnet_id'])
385
385
if subnet['network_id'] != network_id:
391
391
raise n_exc.InvalidInput(error_message=msg)
392
392
subnet_id = subnet['id']
394
is_auto_addr_subnet = ipv6_utils.is_auto_address_subnet(subnet)
394
395
if 'ip_address' in fixed:
395
396
# Ensure that the IP's are unique
396
397
if not NeutronDbPluginV2._check_unique_ip(context, network_id,
403
404
if (not found and
404
405
not self._check_subnet_ip(subnet['cidr'],
405
406
fixed['ip_address'])):
406
msg = _('IP address %s is not a valid IP for the defined '
407
'subnet') % fixed['ip_address']
408
raise n_exc.InvalidInput(error_message=msg)
409
if (ipv6_utils.is_auto_address_subnet(subnet) and
407
raise n_exc.InvalidIpForSubnet(
408
ip_address=fixed['ip_address'])
409
if (is_auto_addr_subnet and
410
410
device_owner not in
411
411
constants.ROUTER_INTERFACE_OWNERS):
412
412
msg = (_("IPv6 address %(address)s can not be directly "
418
418
fixed_ip_set.append({'subnet_id': subnet_id,
419
419
'ip_address': fixed['ip_address']})
421
fixed_ip_set.append({'subnet_id': subnet_id})
421
# A scan for auto-address subnets on the network is done
422
# separately so that all such subnets (not just those
423
# listed explicitly here by subnet ID) are associated
425
if (device_owner in constants.ROUTER_INTERFACE_OWNERS or
426
device_owner == constants.DEVICE_OWNER_ROUTER_SNAT or
427
not is_auto_addr_subnet):
428
fixed_ip_set.append({'subnet_id': subnet_id})
422
430
if len(fixed_ip_set) > cfg.CONF.max_fixed_ips_per_port:
423
431
msg = _('Exceeded maximim amount of fixed ips per port')
424
432
raise n_exc.InvalidInput(error_message=msg)
508
519
ips = self._allocate_fixed_ips(context,
510
521
p['mac_address'])
523
# For ports that are not router ports, implicitly include all
524
# auto-address subnets for address association.
525
if (not p['device_owner'] in constants.ROUTER_INTERFACE_OWNERS and
526
p['device_owner'] != constants.DEVICE_OWNER_ROUTER_SNAT):
527
v6_stateless += [subnet for subnet in subnets
528
if ipv6_utils.is_auto_address_subnet(subnet)]
512
filter = {'network_id': [p['network_id']]}
513
subnets = self.get_subnets(context, filters=filter)
514
# Split into v4 and v6 subnets
530
# Split into v4, v6 stateless and v6 stateful subnets
518
533
for subnet in subnets:
519
534
if subnet['ip_version'] == 4:
520
535
v4.append(subnet)
525
540
v6_stateful.append(subnet)
527
for subnet in v6_stateless:
528
prefix = subnet['cidr']
529
ip_address = ipv6_utils.get_ipv6_addr_by_EUI64(
530
prefix, p['mac_address'])
531
if not self._check_unique_ip(
532
context, p['network_id'],
533
subnet['id'], ip_address.format()):
534
raise n_exc.IpAddressInUse(
535
net_id=p['network_id'],
536
ip_address=ip_address.format())
537
ips.append({'ip_address': ip_address.format(),
538
'subnet_id': subnet['id']})
539
542
version_subnets = [v4, v6_stateful]
540
543
for subnets in version_subnets:
542
545
result = NeutronDbPluginV2._generate_ip(context, subnets)
543
546
ips.append({'ip_address': result['ip_address'],
544
547
'subnet_id': result['subnet_id']})
549
for subnet in v6_stateless:
550
# IP addresses for IPv6 SLAAC and DHCPv6-stateless subnets
551
# are implicitly included.
552
prefix = subnet['cidr']
553
ip_address = ipv6_utils.get_ipv6_addr_by_EUI64(prefix,
555
if not self._check_unique_ip(context, p['network_id'],
556
subnet['id'], ip_address.format()):
557
raise n_exc.IpAddressInUse(net_id=p['network_id'],
558
ip_address=ip_address.format())
559
ips.append({'ip_address': ip_address.format(),
560
'subnet_id': subnet['id']})
547
564
def _validate_subnet_cidr(self, context, network, new_subnet_cidr):
782
799
'name': network['name'],
783
800
'tenant_id': network['tenant_id'],
784
801
'admin_state_up': network['admin_state_up'],
802
'mtu': network.get('mtu', constants.DEFAULT_NETWORK_MTU),
785
803
'status': network['status'],
786
804
'shared': network['shared'],
805
'vlan_transparent': network['vlan_transparent'],
787
806
'subnets': [subnet['id']
788
807
for subnet in network['subnets']]}
789
808
# Call auxiliary extend functions, if any
870
889
'id': n.get('id') or uuidutils.generate_uuid(),
871
890
'name': n['name'],
872
891
'admin_state_up': n['admin_state_up'],
892
'mtu': n.get('mtu', constants.DEFAULT_NETWORK_MTU),
873
893
'shared': n['shared'],
894
'vlan_transparent': n.get('vlan_transparent', False),
874
895
'status': n.get('status', constants.NET_STATUS_ACTIVE)}
875
896
network = models_v2.Network(**args)
876
897
context.session.add(network)
1222
1243
models_v2.IPAllocation).filter_by(
1223
1244
subnet_id=subnet_id).join(models_v2.Port).first()
1246
def _subnet_check_ip_allocations_internal_router_ports(self, context,
1248
# Do not delete the subnet if IP allocations for internal
1249
# router ports still exist
1250
allocs = context.session.query(models_v2.IPAllocation).filter_by(
1251
subnet_id=subnet_id).join(models_v2.Port).filter(
1252
models_v2.Port.device_owner.in_(
1253
constants.ROUTER_INTERFACE_OWNERS)
1256
LOG.debug("Subnet %s still has internal router ports, "
1257
"cannot delete", subnet_id)
1258
raise n_exc.SubnetInUse(subnet_id=id)
1225
1260
def delete_subnet(self, context, id):
1226
1261
with context.session.begin(subtransactions=True):
1227
1262
subnet = self._get_subnet(context, id)
1234
1269
# for IPv6 addresses which were automatically generated
1236
1271
is_auto_addr_subnet = ipv6_utils.is_auto_address_subnet(subnet)
1237
if not is_auto_addr_subnet:
1272
if is_auto_addr_subnet:
1273
self._subnet_check_ip_allocations_internal_router_ports(
1238
1276
qry_network_ports = (
1239
1277
qry_network_ports.filter(models_v2.Port.device_owner.
1240
1278
in_(AUTO_DELETE_PORT_OWNERS)))
1248
1286
# the isolation level is set to READ COMMITTED allocations made
1249
1287
# concurrently will be returned by this query
1250
1288
if not is_auto_addr_subnet:
1251
if self._subnet_check_ip_allocations(context, id):
1252
LOG.debug("Found IP allocations on subnet %s, "
1253
"cannot delete", id)
1289
alloc = self._subnet_check_ip_allocations(context, id)
1291
LOG.info(_LI("Found IP allocation %(alloc)s on subnet "
1292
"%(subnet)s, cannot delete"),
1254
1295
raise n_exc.SubnetInUse(subnet_id=id)
1256
1297
context.session.delete(subnet)
1321
1362
# NOTE(jkoelker) Get the tenant_id outside of the session to avoid
1322
1363
# unneeded db action if the operation raises
1323
1364
tenant_id = self._get_tenant_id_for_create(context, p)
1324
if p.get('device_owner') == constants.DEVICE_OWNER_ROUTER_INTF:
1325
self._enforce_device_owner_not_router_intf_or_device_id(context, p,
1365
if p.get('device_owner'):
1366
self._enforce_device_owner_not_router_intf_or_device_id(
1367
context, p.get('device_owner'), p.get('device_id'), tenant_id)
1328
1369
port_data = dict(tenant_id=tenant_id,
1329
1370
name=p['name'],
1361
1402
p = port['port']
1363
1404
changed_ips = False
1364
changed_device_id = False
1365
1405
with context.session.begin(subtransactions=True):
1366
1406
port = self._get_port(context, id)
1367
if 'device_owner' in p:
1368
current_device_owner = p['device_owner']
1369
changed_device_owner = True
1371
current_device_owner = port['device_owner']
1372
changed_device_owner = False
1373
if p.get('device_id') != port['device_id']:
1374
changed_device_id = True
1407
changed_owner = 'device_owner' in p
1408
current_owner = p.get('device_owner') or port['device_owner']
1409
changed_device_id = p.get('device_id') != port['device_id']
1410
current_device_id = p.get('device_id') or port['device_id']
1376
# if the current device_owner is ROUTER_INF and the device_id or
1377
# device_owner changed check device_id is not another tenants
1379
if ((current_device_owner == constants.DEVICE_OWNER_ROUTER_INTF)
1380
and (changed_device_id or changed_device_owner)):
1412
if current_owner and changed_device_id or changed_owner:
1381
1413
self._enforce_device_owner_not_router_intf_or_device_id(
1382
context, p, port['tenant_id'], port)
1414
context, current_owner, current_device_id,
1384
1417
new_mac = p.get('mac_address')
1385
1418
if new_mac and new_mac != port['mac_address']:
1386
1419
self._check_mac_addr_update(
1387
context, port, new_mac, current_device_owner)
1420
context, port, new_mac, current_owner)
1389
1422
# Check if the IPs need to be updated
1390
1423
network_id = port['network_id']
1490
1523
return self._get_ports_query(context, filters).count()
1492
1525
def _enforce_device_owner_not_router_intf_or_device_id(self, context,
1529
"""Prevent tenants from replacing the device id of router ports with
1530
a router uuid belonging to another tenant.
1532
if device_owner not in constants.ROUTER_INTERFACE_OWNERS:
1496
1534
if not context.is_admin:
1497
# find the device_id. If the call was update_port and the
1498
# device_id was not passed in we use the device_id from the
1500
device_id = port_request.get('device_id')
1501
if not device_id and db_port:
1502
device_id = db_port.get('device_id')
1503
1535
# check to make sure device_id does not match another tenants