Merge "Initial V2 implementation of provider extension."
This commit is contained in:
commit
50c770ef01
@ -2,6 +2,10 @@
|
||||
"admin_or_owner": [["role:admin"], ["tenant_id:%(tenant_id)s"]],
|
||||
"default": [["rule:admin_or_owner"]],
|
||||
|
||||
"admin_api": [["role:admin"]],
|
||||
"extension:provider_network:view": [["rule:admin_api"]],
|
||||
"extension:provider_network:set": [["rule:admin_api"]],
|
||||
|
||||
"create_subnet": [],
|
||||
"get_subnet": [["rule:admin_or_owner"]],
|
||||
"update_subnet": [["rule:admin_or_owner"]],
|
||||
|
@ -346,7 +346,8 @@ class Controller(object):
|
||||
for attr, attr_vals in self._attr_info.iteritems():
|
||||
# Convert values if necessary
|
||||
if ('convert_to' in attr_vals and
|
||||
attr in res_dict):
|
||||
attr in res_dict and
|
||||
res_dict[attr] != attributes.ATTR_NOT_SPECIFIED):
|
||||
res_dict[attr] = attr_vals['convert_to'](res_dict[attr])
|
||||
|
||||
# Check that configured values are correct
|
||||
|
@ -105,6 +105,11 @@ class IpAddressInUse(InUse):
|
||||
"The IP address %(ip_address)s is in use.")
|
||||
|
||||
|
||||
class VlanIdInUse(InUse):
|
||||
message = _("Unable to complete operation for network %(net_id)s. "
|
||||
"The VLAN %(vlan_id)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 "
|
||||
|
66
quantum/extensions/providernet.py
Normal file
66
quantum/extensions/providernet.py
Normal file
@ -0,0 +1,66 @@
|
||||
# Copyright (c) 2012 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.
|
||||
|
||||
from quantum.api.v2 import attributes
|
||||
|
||||
EXTENDED_ATTRIBUTES_2_0 = {
|
||||
'networks': {
|
||||
# TODO(rkukura): specify validation
|
||||
'provider:vlan_id': {'allow_post': True, 'allow_put': False,
|
||||
'convert_to': int,
|
||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||
'is_visible': True},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Providernet(object):
|
||||
"""Extension class supporting provider networks.
|
||||
|
||||
This class is used by quantum's extension framework to make
|
||||
metadata about the provider network extension available to
|
||||
clients. No new resources are defined by this extension. Instead,
|
||||
the existing network resource's request and response messages are
|
||||
extended with attributes in the provider namespace.
|
||||
|
||||
To create a provider VLAN network using the CLI with admin rights:
|
||||
|
||||
(shell) net-create --tenant_id <tenant-id> <net-name> \
|
||||
--provider:vlan_id <vlan-id>
|
||||
|
||||
With admin rights, network dictionaries returned from CLI commands
|
||||
will also include provider attributes.
|
||||
"""
|
||||
|
||||
def get_name(cls):
|
||||
return "Provider Network"
|
||||
|
||||
def get_alias(cls):
|
||||
return "provider"
|
||||
|
||||
def get_description(cls):
|
||||
return "Expose mapping of virtual networks to VLANs and flat networks"
|
||||
|
||||
def get_namespace(cls):
|
||||
return "http://docs.openstack.org/ext/provider/api/v1.0"
|
||||
|
||||
def get_updated(cls):
|
||||
return "2012-07-23T10:00:00-00:00"
|
||||
|
||||
def get_extended_attributes(self, version):
|
||||
if version == "2.0":
|
||||
return EXTENDED_ATTRIBUTES_2_0
|
||||
else:
|
||||
return {}
|
@ -50,7 +50,7 @@ def initialize(base=None):
|
||||
|
||||
|
||||
def create_vlanids():
|
||||
"""Prepopulates the vlan_bindings table"""
|
||||
"""Prepopulate the vlan_bindings table"""
|
||||
LOG.debug("create_vlanids() called")
|
||||
session = db.get_session()
|
||||
start = CONF.VLANS.vlan_start
|
||||
@ -87,7 +87,7 @@ def create_vlanids():
|
||||
|
||||
|
||||
def get_all_vlanids():
|
||||
"""Gets all the vlanids"""
|
||||
"""Get all the vlanids"""
|
||||
LOG.debug("get_all_vlanids() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -99,7 +99,7 @@ def get_all_vlanids():
|
||||
|
||||
|
||||
def is_vlanid_used(vlan_id):
|
||||
"""Checks if a vlanid is in use"""
|
||||
"""Check if a vlanid is in use"""
|
||||
LOG.debug("is_vlanid_used() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -112,7 +112,7 @@ def is_vlanid_used(vlan_id):
|
||||
|
||||
|
||||
def release_vlanid(vlan_id):
|
||||
"""Sets the vlanid state to be unused"""
|
||||
"""Set the vlanid state to be unused, and delete if not in range"""
|
||||
LOG.debug("release_vlanid() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -120,7 +120,10 @@ def release_vlanid(vlan_id):
|
||||
filter_by(vlan_id=vlan_id).
|
||||
one())
|
||||
vlanid["vlan_used"] = False
|
||||
session.merge(vlanid)
|
||||
if vlan_id >= CONF.VLANS.vlan_start and vlan_id <= CONF.VLANS.vlan_end:
|
||||
session.merge(vlanid)
|
||||
else:
|
||||
session.delete(vlanid)
|
||||
session.flush()
|
||||
return vlanid["vlan_used"]
|
||||
except exc.NoResultFound:
|
||||
@ -129,7 +132,7 @@ def release_vlanid(vlan_id):
|
||||
|
||||
|
||||
def delete_vlanid(vlan_id):
|
||||
"""Deletes a vlanid entry from db"""
|
||||
"""Delete a vlanid entry from db"""
|
||||
LOG.debug("delete_vlanid() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -144,7 +147,7 @@ def delete_vlanid(vlan_id):
|
||||
|
||||
|
||||
def reserve_vlanid():
|
||||
"""Reserves the first unused vlanid"""
|
||||
"""Reserve the first unused vlanid"""
|
||||
LOG.debug("reserve_vlanid() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -170,8 +173,32 @@ def reserve_vlanid():
|
||||
raise c_exc.VlanIDNotAvailable()
|
||||
|
||||
|
||||
def reserve_specific_vlanid(vlan_id, net_id):
|
||||
"""Reserve a specific vlanid"""
|
||||
LOG.debug("reserve_specific_vlanid() called")
|
||||
if vlan_id < 1 or vlan_id > 4094:
|
||||
msg = _("Specified VLAN %s outside legal range (1-4094)") % vlan_id
|
||||
raise q_exc.InvalidInput(error_message=msg)
|
||||
session = db.get_session()
|
||||
try:
|
||||
rvlanid = (session.query(l2network_models.VlanID).
|
||||
filter_by(vlan_id=vlan_id).
|
||||
one())
|
||||
if rvlanid["vlan_used"]:
|
||||
raise q_exc.VlanIdInUse(net_id=net_id, vlan_id=vlan_id)
|
||||
LOG.debug("reserving dynamic vlanid %s" % vlan_id)
|
||||
rvlanid["vlan_used"] = True
|
||||
session.merge(rvlanid)
|
||||
except exc.NoResultFound:
|
||||
rvlanid = l2network_models.VlanID(vlan_id)
|
||||
LOG.debug("reserving non-dynamic vlanid %s" % vlan_id)
|
||||
rvlanid["vlan_used"] = True
|
||||
session.add(rvlanid)
|
||||
session.flush()
|
||||
|
||||
|
||||
def get_all_vlanids_used():
|
||||
"""Gets all the vlanids used"""
|
||||
"""Get all the vlanids used"""
|
||||
LOG.debug("get_all_vlanids() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -184,7 +211,7 @@ def get_all_vlanids_used():
|
||||
|
||||
|
||||
def get_all_vlan_bindings():
|
||||
"""Lists all the vlan to network associations"""
|
||||
"""List all the vlan to network associations"""
|
||||
LOG.debug("get_all_vlan_bindings() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -196,7 +223,7 @@ def get_all_vlan_bindings():
|
||||
|
||||
|
||||
def get_vlan_binding(netid):
|
||||
"""Lists the vlan given a network_id"""
|
||||
"""List the vlan given a network_id"""
|
||||
LOG.debug("get_vlan_binding() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -209,7 +236,7 @@ def get_vlan_binding(netid):
|
||||
|
||||
|
||||
def add_vlan_binding(vlanid, netid):
|
||||
"""Adds a vlan to network association"""
|
||||
"""Add a vlan to network association"""
|
||||
LOG.debug("add_vlan_binding() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -226,7 +253,7 @@ def add_vlan_binding(vlanid, netid):
|
||||
|
||||
|
||||
def remove_vlan_binding(netid):
|
||||
"""Removes a vlan to network association"""
|
||||
"""Remove a vlan to network association"""
|
||||
LOG.debug("remove_vlan_binding() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
@ -241,7 +268,7 @@ def remove_vlan_binding(netid):
|
||||
|
||||
|
||||
def update_vlan_binding(netid, newvlanid=None):
|
||||
"""Updates a vlan to network association"""
|
||||
"""Update a vlan to network association"""
|
||||
LOG.debug("update_vlan_binding() called")
|
||||
session = db.get_session()
|
||||
try:
|
||||
|
@ -15,40 +15,94 @@
|
||||
|
||||
import logging
|
||||
|
||||
from quantum.api.v2 import attributes
|
||||
from quantum.db import db_base_plugin_v2
|
||||
from quantum.db import models_v2
|
||||
from quantum.plugins.linuxbridge.db import l2network_db as cdb
|
||||
from quantum import policy
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
"""Implement the Quantum abstractions using Linux bridging.
|
||||
|
||||
A new VLAN is created for each network. An agent is relied upon
|
||||
to perform the actual Linux bridge configuration on each host.
|
||||
|
||||
The provider extension is also supported. As discussed in
|
||||
https://bugs.launchpad.net/quantum/+bug/1023156, this class could
|
||||
be simplified, and filtering on extended attributes could be
|
||||
handled, by adding support for extended attributes to the
|
||||
QuantumDbPluginV2 base class. When that occurs, this class should
|
||||
be updated to take advantage of it.
|
||||
"""
|
||||
LinuxBridgePlugin provides support for Quantum abstractions
|
||||
using LinuxBridge. A new VLAN is created for each network.
|
||||
It relies on an agent to perform the actual bridge configuration
|
||||
on each host.
|
||||
"""
|
||||
|
||||
supported_extension_aliases = ["provider"]
|
||||
|
||||
def __init__(self):
|
||||
cdb.initialize(base=models_v2.model_base.BASEV2)
|
||||
LOG.debug("Linux Bridge Plugin initialization complete")
|
||||
|
||||
# TODO(rkukura) Use core mechanism for attribute authorization
|
||||
# when available.
|
||||
|
||||
def _check_provider_view_auth(self, context, network):
|
||||
return policy.check(context,
|
||||
"extension:provider_network:view",
|
||||
network)
|
||||
|
||||
def _enforce_provider_set_auth(self, context, network):
|
||||
return policy.enforce(context,
|
||||
"extension:provider_network:set",
|
||||
network)
|
||||
|
||||
def _extend_network_dict(self, context, network):
|
||||
if self._check_provider_view_auth(context, network):
|
||||
vlan_binding = cdb.get_vlan_binding(network['id'])
|
||||
network['provider:vlan_id'] = vlan_binding['vlan_id']
|
||||
|
||||
def create_network(self, context, network):
|
||||
new_network = super(LinuxBridgePluginV2, self).create_network(context,
|
||||
network)
|
||||
net = super(LinuxBridgePluginV2, self).create_network(context,
|
||||
network)
|
||||
try:
|
||||
vlan_id = cdb.reserve_vlanid()
|
||||
cdb.add_vlan_binding(vlan_id, new_network['id'])
|
||||
vlan_id = network['network'].get('provider:vlan_id')
|
||||
if vlan_id not in (None, attributes.ATTR_NOT_SPECIFIED):
|
||||
self._enforce_provider_set_auth(context, net)
|
||||
cdb.reserve_specific_vlanid(int(vlan_id), net['id'])
|
||||
else:
|
||||
vlan_id = cdb.reserve_vlanid()
|
||||
cdb.add_vlan_binding(vlan_id, net['id'])
|
||||
self._extend_network_dict(context, net)
|
||||
except:
|
||||
super(LinuxBridgePluginV2, self).delete_network(context,
|
||||
new_network['id'])
|
||||
net['id'])
|
||||
raise
|
||||
|
||||
return new_network
|
||||
return net
|
||||
|
||||
def update_network(self, context, id, network):
|
||||
net = super(LinuxBridgePluginV2, self).update_network(context, id,
|
||||
network)
|
||||
self._extend_network_dict(context, net)
|
||||
return net
|
||||
|
||||
def delete_network(self, context, id):
|
||||
vlan_binding = cdb.get_vlan_binding(id)
|
||||
cdb.release_vlanid(vlan_binding['vlan_id'])
|
||||
cdb.remove_vlan_binding(id)
|
||||
return super(LinuxBridgePluginV2, self).delete_network(context, id)
|
||||
|
||||
def get_network(self, context, id, fields=None, verbose=None):
|
||||
net = super(LinuxBridgePluginV2, self).get_network(context, id,
|
||||
None, verbose)
|
||||
self._extend_network_dict(context, net)
|
||||
return self._fields(net, fields)
|
||||
|
||||
def get_networks(self, context, filters=None, fields=None, verbose=None):
|
||||
nets = super(LinuxBridgePluginV2, self).get_networks(context, filters,
|
||||
None, verbose)
|
||||
for net in nets:
|
||||
self._extend_network_dict(context, net)
|
||||
# TODO(rkukura): Filter on extended attributes.
|
||||
return [self._fields(net, fields) for net in nets]
|
||||
|
@ -21,9 +21,10 @@ that tests the database api method calls
|
||||
"""
|
||||
|
||||
import logging
|
||||
import unittest
|
||||
import unittest2 as unittest
|
||||
|
||||
import quantum.db.api as db
|
||||
import quantum.plugins.linuxbridge.common.exceptions as c_exc
|
||||
import quantum.plugins.linuxbridge.db.l2network_db as l2network_db
|
||||
|
||||
|
||||
@ -157,16 +158,14 @@ class L2networkDBTest(unittest.TestCase):
|
||||
"""Tear Down"""
|
||||
db.clear_db()
|
||||
|
||||
def testa_create_vlanbinding(self):
|
||||
"""test add vlan binding"""
|
||||
def test_create_vlanbinding(self):
|
||||
net1 = self.quantum.create_network("t1", "netid1")
|
||||
vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
|
||||
self.assertTrue(vlan1["vlan-id"] == "10")
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def testb_getall_vlanbindings(self):
|
||||
"""test get all vlan binding"""
|
||||
def test_getall_vlanbindings(self):
|
||||
net1 = self.quantum.create_network("t1", "netid1")
|
||||
net2 = self.quantum.create_network("t1", "netid2")
|
||||
vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
|
||||
@ -178,8 +177,7 @@ class L2networkDBTest(unittest.TestCase):
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def testc_delete_vlanbinding(self):
|
||||
"""test delete vlan binding"""
|
||||
def test_delete_vlanbinding(self):
|
||||
net1 = self.quantum.create_network("t1", "netid1")
|
||||
vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
|
||||
self.assertTrue(vlan1["vlan-id"] == "10")
|
||||
@ -193,8 +191,7 @@ class L2networkDBTest(unittest.TestCase):
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def testd_update_vlanbinding(self):
|
||||
"""test update vlan binding"""
|
||||
def test_update_vlanbinding(self):
|
||||
net1 = self.quantum.create_network("t1", "netid1")
|
||||
vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
|
||||
self.assertTrue(vlan1["vlan-id"] == "10")
|
||||
@ -203,17 +200,55 @@ class L2networkDBTest(unittest.TestCase):
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def teste_test_vlanids(self):
|
||||
"""test vlanid methods"""
|
||||
def test_vlanids(self):
|
||||
l2network_db.create_vlanids()
|
||||
vlanids = l2network_db.get_all_vlanids()
|
||||
self.assertTrue(len(vlanids) > 0)
|
||||
self.assertGreater(len(vlanids), 0)
|
||||
vlanid = l2network_db.reserve_vlanid()
|
||||
used = l2network_db.is_vlanid_used(vlanid)
|
||||
self.assertTrue(used)
|
||||
used = l2network_db.release_vlanid(vlanid)
|
||||
self.assertFalse(used)
|
||||
#counting on default teardown here to clear db
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def test_specific_vlanid_outside(self):
|
||||
l2network_db.create_vlanids()
|
||||
orig_count = len(l2network_db.get_all_vlanids())
|
||||
self.assertGreater(orig_count, 0)
|
||||
vlan_id = 7 # outside range dynamically allocated
|
||||
with self.assertRaises(c_exc.VlanIDNotFound):
|
||||
l2network_db.is_vlanid_used(vlan_id)
|
||||
l2network_db.reserve_specific_vlanid(vlan_id, "net-id")
|
||||
self.assertTrue(l2network_db.is_vlanid_used(vlan_id))
|
||||
count = len(l2network_db.get_all_vlanids())
|
||||
self.assertEqual(count, orig_count + 1)
|
||||
used = l2network_db.release_vlanid(vlan_id)
|
||||
self.assertFalse(used)
|
||||
with self.assertRaises(c_exc.VlanIDNotFound):
|
||||
l2network_db.is_vlanid_used(vlan_id)
|
||||
count = len(l2network_db.get_all_vlanids())
|
||||
self.assertEqual(count, orig_count)
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def test_specific_vlanid_inside(self):
|
||||
l2network_db.create_vlanids()
|
||||
orig_count = len(l2network_db.get_all_vlanids())
|
||||
self.assertGreater(orig_count, 0)
|
||||
vlan_id = 1007 # inside range dynamically allocated
|
||||
self.assertFalse(l2network_db.is_vlanid_used(vlan_id))
|
||||
l2network_db.reserve_specific_vlanid(vlan_id, "net-id")
|
||||
self.assertTrue(l2network_db.is_vlanid_used(vlan_id))
|
||||
count = len(l2network_db.get_all_vlanids())
|
||||
self.assertEqual(count, orig_count)
|
||||
used = l2network_db.release_vlanid(vlan_id)
|
||||
self.assertFalse(used)
|
||||
self.assertFalse(l2network_db.is_vlanid_used(vlan_id))
|
||||
count = len(l2network_db.get_all_vlanids())
|
||||
self.assertEqual(count, orig_count)
|
||||
self.teardown_vlanbinding()
|
||||
self.teardown_network()
|
||||
|
||||
def teardown_network(self):
|
||||
"""tearDown Network table"""
|
||||
|
@ -31,6 +31,17 @@ def get_vlans():
|
||||
return [(binding.vlan_id, binding.network_id) for binding in bindings]
|
||||
|
||||
|
||||
def get_vlan(net_id):
|
||||
session = db.get_session()
|
||||
try:
|
||||
binding = (session.query(ovs_models_v2.VlanBinding).
|
||||
filter_by(network_id=net_id).
|
||||
one())
|
||||
except exc.NoResultFound:
|
||||
return
|
||||
return binding.vlan_id
|
||||
|
||||
|
||||
def add_vlan_binding(vlan_id, net_id):
|
||||
session = db.get_session()
|
||||
binding = ovs_models_v2.VlanBinding(vlan_id, net_id)
|
||||
|
@ -18,11 +18,13 @@
|
||||
# @author: Dan Wendlandt, Nicira Networks, Inc.
|
||||
# @author: Dave Lapsley, Nicira Networks, Inc.
|
||||
# @author: Aaron Rosen, Nicira Networks, Inc.
|
||||
# @author: Bob Kukura, Red Hat, Inc.
|
||||
|
||||
import logging
|
||||
import os
|
||||
|
||||
from quantum.api.api_common import OperationalStatus
|
||||
from quantum.api.v2 import attributes
|
||||
from quantum.common import exceptions as q_exc
|
||||
from quantum.common.utils import find_config_file
|
||||
from quantum.db import api as db
|
||||
@ -32,6 +34,7 @@ from quantum.plugins.openvswitch.common import config
|
||||
from quantum.plugins.openvswitch import ovs_db
|
||||
from quantum.plugins.openvswitch import ovs_db_v2
|
||||
from quantum.quantum_plugin_base import QuantumPluginBase
|
||||
from quantum import policy
|
||||
|
||||
|
||||
LOG = logging.getLogger("ovs_quantum_plugin")
|
||||
@ -80,10 +83,23 @@ class VlanMap(object):
|
||||
raise NoFreeVLANException("No VLAN free for network %s" %
|
||||
network_id)
|
||||
|
||||
def acquire_specific(self, vlan_id, network_id):
|
||||
LOG.debug("Allocating specific VLAN %s for network %s"
|
||||
% (vlan_id, network_id))
|
||||
if vlan_id < 1 or vlan_id > 4094:
|
||||
msg = _("Specified VLAN %s outside legal range (1-4094)") % vlan_id
|
||||
raise q_exc.InvalidInput(error_message=msg)
|
||||
if self.vlans.get(vlan_id):
|
||||
raise q_exc.VlanIdInUse(net_id=network_id,
|
||||
vlan_id=vlan_id)
|
||||
self.free_vlans.discard(vlan_id)
|
||||
self.set_vlan(vlan_id, network_id)
|
||||
|
||||
def release(self, network_id):
|
||||
vlan = self.net_ids.get(network_id, None)
|
||||
if vlan is not None:
|
||||
self.free_vlans.add(vlan)
|
||||
if vlan >= self.vlan_min and vlan <= self.vlan_max:
|
||||
self.free_vlans.add(vlan)
|
||||
del self.vlans[vlan]
|
||||
del self.net_ids[network_id]
|
||||
LOG.debug("Deallocated VLAN %s (used by network %s)" %
|
||||
@ -234,8 +250,26 @@ class OVSQuantumPlugin(QuantumPluginBase):
|
||||
|
||||
|
||||
class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
"""Implement the Quantum abstractions using Open vSwitch.
|
||||
|
||||
Depending on whether tunneling is enabled, either a GRE tunnel or
|
||||
a new VLAN is created for each network. An agent is relied upon to
|
||||
perform the actual OVS configuration on each host.
|
||||
|
||||
The provider extension is also supported. As discussed in
|
||||
https://bugs.launchpad.net/quantum/+bug/1023156, this class could
|
||||
be simplified, and filtering on extended attributes could be
|
||||
handled, by adding support for extended attributes to the
|
||||
QuantumDbPluginV2 base class. When that occurs, this class should
|
||||
be updated to take advantage of it.
|
||||
"""
|
||||
|
||||
supported_extension_aliases = ["provider"]
|
||||
|
||||
def __init__(self, configfile=None):
|
||||
conf = config.parse(CONF_FILE)
|
||||
self.enable_tunneling = conf.OVS.enable_tunneling
|
||||
|
||||
options = {"sql_connection": conf.DATABASE.sql_connection}
|
||||
options.update({'base': models_v2.model_base.BASEV2})
|
||||
sql_max_retries = conf.DATABASE.sql_max_retries
|
||||
@ -247,19 +281,63 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
||||
self.vmap = VlanMap(conf.OVS.vlan_min, conf.OVS.vlan_max)
|
||||
self.vmap.populate_already_used(ovs_db_v2.get_vlans())
|
||||
|
||||
# TODO(rkukura) Use core mechanism for attribute authorization
|
||||
# when available.
|
||||
|
||||
def _check_provider_view_auth(self, context, network):
|
||||
return policy.check(context,
|
||||
"extension:provider_network:view",
|
||||
network)
|
||||
|
||||
def _enforce_provider_set_auth(self, context, network):
|
||||
return policy.enforce(context,
|
||||
"extension:provider_network:set",
|
||||
network)
|
||||
|
||||
def _extend_network_dict(self, context, network):
|
||||
if self._check_provider_view_auth(context, network):
|
||||
if not self.enable_tunneling:
|
||||
network['provider:vlan_id'] = ovs_db_v2.get_vlan(network['id'])
|
||||
|
||||
def create_network(self, context, network):
|
||||
net = super(OVSQuantumPluginV2, self).create_network(context, network)
|
||||
try:
|
||||
vlan_id = self.vmap.acquire(str(net['id']))
|
||||
except NoFreeVLANException:
|
||||
vlan_id = network['network'].get('provider:vlan_id')
|
||||
if vlan_id not in (None, attributes.ATTR_NOT_SPECIFIED):
|
||||
self._enforce_provider_set_auth(context, net)
|
||||
self.vmap.acquire_specific(int(vlan_id), str(net['id']))
|
||||
else:
|
||||
vlan_id = self.vmap.acquire(str(net['id']))
|
||||
except Exception:
|
||||
super(OVSQuantumPluginV2, self).delete_network(context, net['id'])
|
||||
raise
|
||||
|
||||
LOG.debug("Created network: %s" % net['id'])
|
||||
ovs_db_v2.add_vlan_binding(vlan_id, str(net['id']))
|
||||
self._extend_network_dict(context, net)
|
||||
return net
|
||||
|
||||
def update_network(self, context, id, network):
|
||||
net = super(OVSQuantumPluginV2, self).update_network(context, id,
|
||||
network)
|
||||
self._extend_network_dict(context, net)
|
||||
return net
|
||||
|
||||
def delete_network(self, context, id):
|
||||
ovs_db_v2.remove_vlan_binding(id)
|
||||
self.vmap.release(id)
|
||||
return super(OVSQuantumPluginV2, self).delete_network(context, id)
|
||||
|
||||
def get_network(self, context, id, fields=None, verbose=None):
|
||||
net = super(OVSQuantumPluginV2, self).get_network(context, id,
|
||||
None, verbose)
|
||||
self._extend_network_dict(context, net)
|
||||
return self._fields(net, fields)
|
||||
|
||||
def get_networks(self, context, filters=None, fields=None, verbose=None):
|
||||
nets = super(OVSQuantumPluginV2, self).get_networks(context, filters,
|
||||
None, verbose)
|
||||
for net in nets:
|
||||
self._extend_network_dict(context, net)
|
||||
# TODO(rkukura): Filter on extended attributes.
|
||||
return [self._fields(net, fields) for net in nets]
|
||||
|
Loading…
Reference in New Issue
Block a user