
bug 942713. This bug confuses the validate_networks() method of QuantumManager in Nova, causing it to believe that it is valid for a tenant to plug into a particular network when in fact that network is not owned by the tenant, nor the "provider". The patch also adds unit tests to confirm correct plugin behavior. This patch fixes the issue for the Sample Plugin, the OVS plugin, the Linux Bridge plugin, and the Ryu plugin, all of which has the same DB model. Validated the fix with the unit tests. I couldn't run the unit tests for the NVP plugin standalone, but by inspection, the code seems to handle this case. I wasn't able to run the Cisco plugin unit tests, and that code uses its own DB model, so I am uncertain whether this issue exists in that plugin. Change-Id: I8c4a5f3eb151b91a1076821dc1916842510dfb90
183 lines
6.9 KiB
Python
183 lines
6.9 KiB
Python
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
|
#
|
|
# Copyright 2012 Isaku Yamahata
|
|
# 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 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.
|
|
# @author: Isaku Yamahata
|
|
import ConfigParser
|
|
import logging as LOG
|
|
import os
|
|
from abc import ABCMeta, abstractmethod
|
|
|
|
import quantum.db.api as db
|
|
from quantum.api.api_common import OperationalStatus
|
|
from quantum.common import exceptions as q_exc
|
|
from quantum.manager import find_config
|
|
from quantum.quantum_plugin_base import QuantumPluginBase
|
|
|
|
|
|
LOG.getLogger(__name__)
|
|
|
|
|
|
class OVSQuantumPluginDriverBase(object):
|
|
"""
|
|
Base class for OVS quantum plugin driver
|
|
"""
|
|
__metaclass__ = ABCMeta
|
|
|
|
@abstractmethod
|
|
def create_network(self, net):
|
|
pass
|
|
|
|
@abstractmethod
|
|
def delete_network(self, net):
|
|
pass
|
|
|
|
|
|
class OVSQuantumPluginBase(QuantumPluginBase):
|
|
"""
|
|
Base class for OVS-based plugin which referes to a subclass of
|
|
OVSQuantumPluginDriverBase which is defined above.
|
|
Subclass of OVSQuantumPluginBase must set self.driver to a subclass of
|
|
OVSQuantumPluginDriverBase.
|
|
"""
|
|
def __init__(self, conf_file, mod_file, configfile=None):
|
|
super(OVSQuantumPluginBase, self).__init__()
|
|
config = ConfigParser.ConfigParser()
|
|
if configfile is None:
|
|
if conf_file and os.path.exists(conf_file):
|
|
configfile = conf_file
|
|
else:
|
|
configfile = find_config(os.path.abspath(
|
|
os.path.dirname(mod_file)))
|
|
if configfile is None:
|
|
raise Exception("Configuration file \"%s\" doesn't exist" %
|
|
(configfile))
|
|
LOG.debug("Using configuration file: %s", configfile)
|
|
config.read(configfile)
|
|
LOG.debug("Config: %s", config)
|
|
|
|
options = {"sql_connection": config.get("DATABASE", "sql_connection")}
|
|
db.configure_db(options)
|
|
|
|
self.config = config
|
|
# Subclass must set self.driver to its own OVSQuantumPluginDriverBase
|
|
self.driver = None
|
|
|
|
def get_all_networks(self, tenant_id, **kwargs):
|
|
nets = []
|
|
for net in db.network_list(tenant_id):
|
|
LOG.debug("Adding network: %s", net.uuid)
|
|
nets.append(self._make_net_dict(str(net.uuid), net.name,
|
|
None, net.op_status))
|
|
return nets
|
|
|
|
def _make_net_dict(self, net_id, net_name, ports, op_status):
|
|
res = {'net-id': net_id,
|
|
'net-name': net_name,
|
|
'net-op-status': op_status}
|
|
if ports:
|
|
res['net-ports'] = ports
|
|
return res
|
|
|
|
def create_network(self, tenant_id, net_name, **kwargs):
|
|
net = db.network_create(tenant_id, net_name,
|
|
op_status=OperationalStatus.UP)
|
|
LOG.debug("Created network: %s", net)
|
|
self.driver.create_network(net)
|
|
return self._make_net_dict(str(net.uuid), net.name, [], net.op_status)
|
|
|
|
def delete_network(self, tenant_id, net_id):
|
|
db.validate_network_ownership(tenant_id, net_id)
|
|
net = db.network_get(net_id)
|
|
|
|
# Verify that no attachments are plugged into the network
|
|
for port in db.port_list(net_id):
|
|
if port.interface_id:
|
|
raise q_exc.NetworkInUse(net_id=net_id)
|
|
net = db.network_destroy(net_id)
|
|
self.driver.delete_network(net)
|
|
return self._make_net_dict(str(net.uuid), net.name, [], net.op_status)
|
|
|
|
def get_network_details(self, tenant_id, net_id):
|
|
db.validate_network_ownership(tenant_id, net_id)
|
|
net = db.network_get(net_id)
|
|
ports = self.get_all_ports(tenant_id, net_id)
|
|
return self._make_net_dict(str(net.uuid), net.name,
|
|
ports, net.op_status)
|
|
|
|
def update_network(self, tenant_id, net_id, **kwargs):
|
|
db.validate_network_ownership(tenant_id, net_id)
|
|
net = db.network_update(net_id, tenant_id, **kwargs)
|
|
return self._make_net_dict(str(net.uuid), net.name,
|
|
None, net.op_status)
|
|
|
|
def _make_port_dict(self, port):
|
|
if port.state == "ACTIVE":
|
|
op_status = port.op_status
|
|
else:
|
|
op_status = OperationalStatus.DOWN
|
|
|
|
return {'port-id': str(port.uuid),
|
|
'port-state': port.state,
|
|
'port-op-status': op_status,
|
|
'net-id': port.network_id,
|
|
'attachment': port.interface_id}
|
|
|
|
def get_all_ports(self, tenant_id, net_id, **kwargs):
|
|
db.validate_network_ownership(tenant_id, net_id)
|
|
ports = db.port_list(net_id)
|
|
# This plugin does not perform filtering at the moment
|
|
return [{'port-id': str(port.uuid)} for port in ports]
|
|
|
|
def create_port(self, tenant_id, net_id, port_state=None, **kwargs):
|
|
LOG.debug("Creating port with network_id: %s", net_id)
|
|
port = db.port_create(net_id, port_state,
|
|
op_status=OperationalStatus.DOWN)
|
|
return self._make_port_dict(port)
|
|
|
|
def delete_port(self, tenant_id, net_id, port_id):
|
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
|
port = db.port_destroy(port_id, net_id)
|
|
return self._make_port_dict(port)
|
|
|
|
def update_port(self, tenant_id, net_id, port_id, **kwargs):
|
|
"""
|
|
Updates the state of a port on the specified Virtual Network.
|
|
"""
|
|
LOG.debug("update_port() called\n")
|
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
|
port = db.port_get(port_id, net_id)
|
|
db.port_update(port_id, net_id, **kwargs)
|
|
return self._make_port_dict(port)
|
|
|
|
def get_port_details(self, tenant_id, net_id, port_id):
|
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
|
port = db.port_get(port_id, net_id)
|
|
return self._make_port_dict(port)
|
|
|
|
def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id):
|
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
|
db.port_set_attachment(port_id, net_id, remote_iface_id)
|
|
|
|
def unplug_interface(self, tenant_id, net_id, port_id):
|
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
|
db.port_set_attachment(port_id, net_id, "")
|
|
db.port_update(port_id, net_id, op_status=OperationalStatus.DOWN)
|
|
|
|
def get_interface_details(self, tenant_id, net_id, port_id):
|
|
db.validate_port_ownership(tenant_id, net_id, port_id)
|
|
res = db.port_get(port_id, net_id)
|
|
return res.interface_id
|