"is plugged into the logical port.")
+class MacAddressInUse(InUse):
+ message = _("Unable to complete operation for network %(net_id)s. "
+ "The mac address %(mac)s is in use.")
+
+
class AlreadyAttached(QuantumException):
message = _("Unable to plug the attachment %(att_id)s into port "
"%(port_id)s for network %(net_id)s. The attachment is "
class FixedIPNotAvailable(QuantumException):
message = _("Fixed IP (%(ip)s) unavailable for network "
"%(network_uuid)s")
+
+
+class MacAddressGenerationFailure(QuantumException):
+ message = _("Unable to generate unique mac on network %(net_id)s.")
collection = collection.filter(column.in_(value))
return [dict_func(c, fields) for c in collection.all()]
+ @staticmethod
+ def _generate_mac(context, network_id):
+ # TODO(garyk) read from configuration file (CONF)
+ max_retries = 16
+ for i in range(max_retries):
+ # TODO(garyk) read base mac from configuration file (CONF)
+ mac = [0xfa, 0x16, 0x3e, random.randint(0x00, 0x7f),
+ random.randint(0x00, 0xff), random.randint(0x00, 0xff)]
+ mac_address = ':'.join(map(lambda x: "%02x" % x, mac))
+ if QuantumDbPluginV2._check_unique_mac(context, network_id,
+ mac_address):
+ LOG.debug("Generated mac for network %s is %s",
+ network_id, mac_address)
+ return mac_address
+ else:
+ LOG.debug("Generated mac %s exists. Remaining attempts %s.",
+ mac_address, max_retries - (i + 1))
+ LOG.error("Unable to generate mac address after %s attempts",
+ max_retries)
+ raise q_exc.MacAddressGenerationFailure(net_id=network_id)
+
+ @staticmethod
+ def _check_unique_mac(context, network_id, mac_address):
+ mac_qry = context.session.query(models_v2.Port)
+ try:
+ mac_qry.filter_by(network_id=network_id,
+ mac_address=mac_address).one()
+ except exc.NoResultFound:
+ return True
+ return False
+
def _make_network_dict(self, network, fields=None):
res = {'id': network['id'],
'name': network['name'],
# unneeded db action if the operation raises
tenant_id = self._get_tenant_id_for_create(context, p)
- if p['mac_address'] == api_router.ATTR_NOT_SPECIFIED:
- #FIXME(danwent): this is exact Nova mac generation logic
- # we will want to provide more flexibility and to check
- # for uniqueness.
- mac = [0xfa, 0x16, 0x3e, random.randint(0x00, 0x7f),
- random.randint(0x00, 0xff), random.randint(0x00, 0xff)]
- p['mac_address'] = ':'.join(map(lambda x: "%02x" % x, mac))
-
with context.session.begin():
network = self._get_network(context, p["network_id"])
+ # Ensure that a MAC address is defined and it is unique on the
+ # network
+ if p['mac_address'] == api_router.ATTR_NOT_SPECIFIED:
+ p['mac_address'] = QuantumDbPluginV2._generate_mac(
+ context, p["network_id"])
+ else:
+ # Ensure that the mac on the network is unique
+ if not QuantumDbPluginV2._check_unique_mac(context,
+ p["network_id"],
+ p['mac_address']):
+ raise q_exc.MacAddressInUse(net_id=p["network_id"],
+ mac=p['mac_address'])
+
port = models_v2.Port(tenant_id=tenant_id,
network_id=p['network_id'],
mac_address=p['mac_address'],
import logging
import unittest
import contextlib
+import mock
+import quantum
from quantum.api.v2.router import APIRouter
+from quantum.common import exceptions as q_exc
from quantum.db import api as db
from quantum.tests.unit.testlib_api import create_request
from quantum.wsgi import Serializer, JSONDeserializer
self.assertEqual(res['port']['admin_state_up'],
data['port']['admin_state_up'])
+ def test_requested_duplicate_mac(self):
+ fmt = 'json'
+ with self.port() as port:
+ mac = port['port']['mac_address']
+ # check that MAC address matches base MAC
+ # TODO(garyk) read base mac from configuration file (CONF)
+ base_mac = [0xfa, 0x16, 0x3e]
+ base_mac_address = ':'.join(map(lambda x: "%02x" % x, base_mac))
+ self.assertTrue(mac.startswith(base_mac_address))
+ kwargs = {"mac_address": mac}
+ net_id = port['port']['network_id']
+ res = self._create_port(fmt, net_id=net_id, **kwargs)
+ port2 = self.deserialize(fmt, res)
+ self.assertEquals(res.status_int, 409)
+
+ def test_mac_exhaustion(self):
+ # rather than actually consuming all MAC (would take a LONG time)
+ # we just raise the exception that would result.
+ @staticmethod
+ def fake_gen_mac(context, net_id):
+ raise q_exc.MacAddressGenerationFailure(net_id=net_id)
+
+ fmt = 'json'
+ with mock.patch.object(quantum.db.db_base_plugin_v2.QuantumDbPluginV2,
+ '_generate_mac', new=fake_gen_mac):
+ res = self._create_network(fmt=fmt, name='net1',
+ admin_status_up=True)
+ network = self.deserialize(fmt, res)
+ net_id = network['network']['id']
+ res = self._create_port(fmt, net_id=net_id)
+ self.assertEquals(res.status_int, 503)
+
class TestNetworksV2(QuantumDbPluginV2TestCase):
# NOTE(cerberus): successful network update and delete are