Merge "Add QoS North Bound Functionality"
This commit is contained in:
commit
f3e56f2132
|
@ -17,7 +17,7 @@
|
||||||
# http://git.openstack.org/cgit/openstack-infra/project-config/tree/jenkins/jobs/dragonflow.yaml
|
# http://git.openstack.org/cgit/openstack-infra/project-config/tree/jenkins/jobs/dragonflow.yaml
|
||||||
#
|
#
|
||||||
|
|
||||||
export OVERRIDE_ENABLED_SERVICES=key,n-api,n-cpu,n-cond,n-sch,n-crt,n-cauth,n-obj,g-api,g-reg,c-sch,c-api,c-vol,horizon,rabbit,mysql,dstat,df-controller,df-redis,df-redis-server,q-svc,df-l3-agent,df-metadata
|
export OVERRIDE_ENABLED_SERVICES=key,n-api,n-cpu,n-cond,n-sch,n-crt,n-cauth,n-obj,g-api,g-reg,c-sch,c-api,c-vol,horizon,rabbit,mysql,dstat,df-controller,df-redis,df-redis-server,q-svc,df-l3-agent,df-metadata,q-qos
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_REDIS_PUBSUB=True"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_REDIS_PUBSUB=True"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_RUNNING_IN_GATE=True"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"DF_RUNNING_IN_GATE=True"
|
||||||
export DEVSTACK_LOCAL_CONFIG+=$'\n'"NEUTRON_CREATE_INITIAL_NETWORKS=False"
|
export DEVSTACK_LOCAL_CONFIG+=$'\n'"NEUTRON_CREATE_INITIAL_NETWORKS=False"
|
||||||
|
|
|
@ -145,6 +145,32 @@ function configure_df_metadata_service {
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function configure_qos {
|
||||||
|
Q_SERVICE_PLUGIN_CLASSES+=",qos"
|
||||||
|
Q_ML2_PLUGIN_EXT_DRIVERS+=",qos"
|
||||||
|
iniset /$Q_PLUGIN_CONF_FILE ml2 extension_drivers "$Q_ML2_PLUGIN_EXT_DRIVERS"
|
||||||
|
|
||||||
|
# NOTE: QoS plugin use "message_queue" as default driver if no
|
||||||
|
# notification driver in neutron.conf, or it will use driver
|
||||||
|
# assigned in neutron.conf.
|
||||||
|
# If "df" is the only mech driver for ml2, we use
|
||||||
|
# "df_notification_driver" as notification driver.
|
||||||
|
# If ml2 has other mech driver except "df", we should add
|
||||||
|
# "message_queue,df_notification_driver" to existing
|
||||||
|
# notification driver
|
||||||
|
if [[ "$Q_ML2_PLUGIN_MECHANISM_DRIVERS" == "df" ]]; then
|
||||||
|
NOTIFICATION_DRIVER="df_notification_driver"
|
||||||
|
else
|
||||||
|
NOTIFICATION_DRIVER=$(iniget ${NEUTRON_CONF} qos notification_drivers)
|
||||||
|
if [[ "$NOTIFICATION_DRIVER" == "" ]]; then
|
||||||
|
NOTIFICATION_DRIVER="message_queue,df_notification_driver"
|
||||||
|
else
|
||||||
|
NOTIFICATION_DRIVER+=",df_notification_driver"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
iniset $NEUTRON_CONF qos notification_drivers "$NOTIFICATION_DRIVER"
|
||||||
|
}
|
||||||
|
|
||||||
function configure_df_plugin {
|
function configure_df_plugin {
|
||||||
echo "Configuring Neutron for Dragonflow"
|
echo "Configuring Neutron for Dragonflow"
|
||||||
|
|
||||||
|
@ -160,6 +186,9 @@ function configure_df_plugin {
|
||||||
Q_SERVICE_PLUGIN_CLASSES=""
|
Q_SERVICE_PLUGIN_CLASSES=""
|
||||||
else
|
else
|
||||||
DF_APPS_LIST=$ML2_APPS_LIST
|
DF_APPS_LIST=$ML2_APPS_LIST
|
||||||
|
if is_service_enabled q-qos ; then
|
||||||
|
configure_qos
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Generate configuration file
|
# Generate configuration file
|
||||||
|
|
|
@ -24,7 +24,7 @@ from dragonflow.common import utils as df_utils
|
||||||
cfg.CONF.register_opts(common_params.DF_OPTS, 'df')
|
cfg.CONF.register_opts(common_params.DF_OPTS, 'df')
|
||||||
|
|
||||||
db_tables = ['lport', 'lswitch', 'lrouter', 'chassis', 'secgroup',
|
db_tables = ['lport', 'lswitch', 'lrouter', 'chassis', 'secgroup',
|
||||||
'tunnel_key', 'floatingip', 'publisher']
|
'tunnel_key', 'floatingip', 'publisher', 'qospolicy']
|
||||||
|
|
||||||
usage_str = "The following commands are supported:\n" \
|
usage_str = "The following commands are supported:\n" \
|
||||||
"1) df-db ls - print all the db tables \n" \
|
"1) df-db ls - print all the db tables \n" \
|
||||||
|
|
|
@ -743,6 +743,40 @@ class NbApi(object):
|
||||||
topic,
|
topic,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create_qos_policy(self, policy_id, topic, **columns):
|
||||||
|
policy = {'id': policy_id,
|
||||||
|
'topic': topic}
|
||||||
|
policy.update(columns)
|
||||||
|
policy_json = jsonutils.dumps(policy)
|
||||||
|
|
||||||
|
self.driver.create_key('qospolicy', policy_id, policy_json, topic)
|
||||||
|
self._send_db_change_event('qospolicy', policy_id, 'create',
|
||||||
|
policy_json, topic)
|
||||||
|
|
||||||
|
def update_qos_policy(self, policy_id, topic, **columns):
|
||||||
|
qospolicy_json = self.driver.get_key('qospolicy', policy_id, topic)
|
||||||
|
policy = jsonutils.loads(qospolicy_json)
|
||||||
|
policy.update(columns)
|
||||||
|
policy_json = jsonutils.dumps(policy)
|
||||||
|
|
||||||
|
self.driver.set_key('qospolicy', policy_id, policy_json, topic)
|
||||||
|
self._send_db_change_event('qospolicy', policy_id, 'set',
|
||||||
|
policy_json, topic)
|
||||||
|
|
||||||
|
def delete_qos_policy(self, policy_id, topic):
|
||||||
|
self.driver.delete_key('qospolicy', policy_id, topic)
|
||||||
|
self._send_db_change_event('qospolicy', policy_id, 'delete', policy_id,
|
||||||
|
topic)
|
||||||
|
|
||||||
|
def get_qos_policy(self, policy_id, topic=None):
|
||||||
|
try:
|
||||||
|
qospolicy_value = self.driver.get_key('qospolicy',
|
||||||
|
policy_id, topic)
|
||||||
|
return QosPolicy(qospolicy_value)
|
||||||
|
except Exception:
|
||||||
|
LOG.exception(_LE('Could not get qos policy %s'), policy_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
@six.add_metaclass(abc.ABCMeta)
|
@six.add_metaclass(abc.ABCMeta)
|
||||||
class DbStoreObject(object):
|
class DbStoreObject(object):
|
||||||
|
@ -927,6 +961,9 @@ class LogicalPort(DbStoreObject):
|
||||||
def get_binding_vnic_type(self):
|
def get_binding_vnic_type(self):
|
||||||
return self.lport.get('binding_vnic_type')
|
return self.lport.get('binding_vnic_type')
|
||||||
|
|
||||||
|
def get_qos_policy_id(self):
|
||||||
|
return self.lport.get('qos_policy_id')
|
||||||
|
|
||||||
def get_version(self):
|
def get_version(self):
|
||||||
return self.lport['version']
|
return self.lport['version']
|
||||||
|
|
||||||
|
@ -1212,3 +1249,57 @@ class Publisher(DbStoreObject):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.publisher)
|
return str(self.publisher)
|
||||||
|
|
||||||
|
|
||||||
|
class QosPolicy(DbStoreObject):
|
||||||
|
|
||||||
|
def __init__(self, value):
|
||||||
|
self.qospolicy = jsonutils.loads(value)
|
||||||
|
|
||||||
|
def get_id(self):
|
||||||
|
return self.qospolicy.get('id')
|
||||||
|
|
||||||
|
def get_name(self):
|
||||||
|
return self.qospolicy.get('name')
|
||||||
|
|
||||||
|
def get_topic(self):
|
||||||
|
return self.qospolicy.get('topic')
|
||||||
|
|
||||||
|
def get_type(self):
|
||||||
|
return self.qospolicy.get('type')
|
||||||
|
|
||||||
|
def get_version(self):
|
||||||
|
return self.qospolicy.get('version')
|
||||||
|
|
||||||
|
def get_max_burst_kbps(self):
|
||||||
|
rules = self.qospolicy.get('rules')
|
||||||
|
max_burst_kbps = None
|
||||||
|
for rule in rules:
|
||||||
|
if rule['type'] == 'bandwidth_limit':
|
||||||
|
max_burst_kbps = rule.get('max_burst_kbps')
|
||||||
|
break
|
||||||
|
|
||||||
|
return max_burst_kbps
|
||||||
|
|
||||||
|
def get_max_kbps(self):
|
||||||
|
rules = self.qospolicy.get('rules')
|
||||||
|
max_kbps = None
|
||||||
|
for rule in rules:
|
||||||
|
if rule['type'] == 'bandwidth_limit':
|
||||||
|
max_kbps = rule.get('max_kbps')
|
||||||
|
break
|
||||||
|
|
||||||
|
return max_kbps
|
||||||
|
|
||||||
|
def get_dscp_marking(self):
|
||||||
|
rules = self.qospolicy.get('rules')
|
||||||
|
dscp_marking = None
|
||||||
|
for rule in rules:
|
||||||
|
if rule['type'] == 'dscp_marking':
|
||||||
|
dscp_marking = rule.get('dscp_mark')
|
||||||
|
break
|
||||||
|
|
||||||
|
return dscp_marking
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return str(self.qospolicy)
|
||||||
|
|
|
@ -47,6 +47,8 @@ RESOURCE_ML2_CORE = 2 # network, subnet, port
|
||||||
RESOURCE_ML2_SECURITY_GROUP = 3
|
RESOURCE_ML2_SECURITY_GROUP = 3
|
||||||
RESOURCE_ML2_SECURITY_GROUP_RULE_CREATE = 4
|
RESOURCE_ML2_SECURITY_GROUP_RULE_CREATE = 4
|
||||||
RESOURCE_ML2_SECURITY_GROUP_RULE_DELETE = 5
|
RESOURCE_ML2_SECURITY_GROUP_RULE_DELETE = 5
|
||||||
|
RESOURCE_QOS_POLICY_CREATE_OR_UPDATE = 6
|
||||||
|
RESOURCE_QOS_POLICY_DELETE = 7
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -107,6 +109,11 @@ def _get_lock_id_by_resource_type(type, *args, **kwargs):
|
||||||
lock_id = args[1]['security_group_rule']['tenant_id']
|
lock_id = args[1]['security_group_rule']['tenant_id']
|
||||||
elif RESOURCE_ML2_SECURITY_GROUP_RULE_DELETE == type:
|
elif RESOURCE_ML2_SECURITY_GROUP_RULE_DELETE == type:
|
||||||
lock_id = args[1]['context'].tenant_id
|
lock_id = args[1]['context'].tenant_id
|
||||||
|
elif RESOURCE_QOS_POLICY_CREATE_OR_UPDATE == type:
|
||||||
|
lock_id = args[0][2]['tenant_id']
|
||||||
|
elif RESOURCE_QOS_POLICY_DELETE == type:
|
||||||
|
# when delete qos policy, there's no tenant_id in args.
|
||||||
|
lock_id = GLOBAL_LOCK_ID
|
||||||
|
|
||||||
if not lock_id:
|
if not lock_id:
|
||||||
lock_id = GLOBAL_LOCK_ID
|
lock_id = GLOBAL_LOCK_ID
|
||||||
|
|
|
@ -246,7 +246,8 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
router_external=network['router:external'],
|
router_external=network['router:external'],
|
||||||
mtu=network.get('mtu'),
|
mtu=network.get('mtu'),
|
||||||
version=network['revision_number'],
|
version=network['revision_number'],
|
||||||
subnets=[])
|
subnets=[],
|
||||||
|
qos_policy_id=network.get('qos_policy_id'))
|
||||||
|
|
||||||
LOG.info(_LI("DFMechDriver: create network %s"), network['id'])
|
LOG.info(_LI("DFMechDriver: create network %s"), network['id'])
|
||||||
return network
|
return network
|
||||||
|
@ -263,9 +264,28 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
except df_exceptions.DBKeyNotFound:
|
except df_exceptions.DBKeyNotFound:
|
||||||
LOG.debug("lswitch %s is not found in DF DB, might have "
|
LOG.debug("lswitch %s is not found in DF DB, might have "
|
||||||
"been deleted concurrently" % network_id)
|
"been deleted concurrently" % network_id)
|
||||||
|
return
|
||||||
|
|
||||||
LOG.info(_LI("DFMechDriver: delete network %s"), network_id)
|
LOG.info(_LI("DFMechDriver: delete network %s"), network_id)
|
||||||
|
|
||||||
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_ML2_CORE)
|
||||||
|
def update_network_postcommit(self, context):
|
||||||
|
network = context.current
|
||||||
|
|
||||||
|
self.nb_api.update_lswitch(
|
||||||
|
id=network['id'],
|
||||||
|
topic=network['tenant_id'],
|
||||||
|
name=network.get('name', df_const.DF_NETWORK_DEFAULT_NAME),
|
||||||
|
network_type=network.get('provider:network_type'),
|
||||||
|
segmentation_id=network.get('provider:segmentation_id'),
|
||||||
|
router_external=network.get('router:external'),
|
||||||
|
mtu=network.get('mtu'),
|
||||||
|
version=network['revision_number'],
|
||||||
|
qos_policy_id=network.get('qos_policy_id'))
|
||||||
|
|
||||||
|
LOG.info(_LI("DFMechDriver: update network %s"), network['id'])
|
||||||
|
return network
|
||||||
|
|
||||||
def _get_dhcp_port_for_subnet(self, context, subnet_id):
|
def _get_dhcp_port_for_subnet(self, context, subnet_id):
|
||||||
filters = {'fixed_ips': {'subnet_id': [subnet_id]},
|
filters = {'fixed_ips': {'subnet_id': [subnet_id]},
|
||||||
'device_owner': [n_const.DEVICE_OWNER_DHCP]}
|
'device_owner': [n_const.DEVICE_OWNER_DHCP]}
|
||||||
|
@ -370,8 +390,9 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
dhcp_ip, dhcp_port = self._handle_create_subnet_dhcp(
|
dhcp_ip, dhcp_port = self._handle_create_subnet_dhcp(
|
||||||
plugin_context,
|
plugin_context,
|
||||||
subnet)
|
subnet)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
LOG.exception(e)
|
LOG.exception(
|
||||||
|
_LE("Failed to create dhcp port for subnet %s"), subnet['id'])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.nb_api.add_subnet(
|
self.nb_api.add_subnet(
|
||||||
|
@ -447,8 +468,10 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
plugin_context,
|
plugin_context,
|
||||||
old_subnet,
|
old_subnet,
|
||||||
new_subnet)
|
new_subnet)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
LOG.exception(e)
|
LOG.exception(
|
||||||
|
_LE("Failed to create dhcp port for subnet %s"),
|
||||||
|
new_subnet['id'])
|
||||||
return None
|
return None
|
||||||
|
|
||||||
self.nb_api.update_subnet(
|
self.nb_api.update_subnet(
|
||||||
|
@ -489,6 +512,7 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
except df_exceptions.DBKeyNotFound:
|
except df_exceptions.DBKeyNotFound:
|
||||||
LOG.debug("network %s is not found in DB, might have "
|
LOG.debug("network %s is not found in DB, might have "
|
||||||
"been deleted concurrently" % net_id)
|
"been deleted concurrently" % net_id)
|
||||||
|
return
|
||||||
|
|
||||||
LOG.info(_LI("DFMechDriver: delete subnet %s"), subnet_id)
|
LOG.info(_LI("DFMechDriver: delete subnet %s"), subnet_id)
|
||||||
|
|
||||||
|
@ -532,7 +556,8 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
remote_vtep=remote_vtep,
|
remote_vtep=remote_vtep,
|
||||||
allowed_address_pairs=port.get(addr_pair.ADDRESS_PAIRS, []),
|
allowed_address_pairs=port.get(addr_pair.ADDRESS_PAIRS, []),
|
||||||
binding_profile=port.get(portbindings.PROFILE, None),
|
binding_profile=port.get(portbindings.PROFILE, None),
|
||||||
binding_vnic_type=port.get(portbindings.VNIC_TYPE, None))
|
binding_vnic_type=port.get(portbindings.VNIC_TYPE, None),
|
||||||
|
qos_policy_id=port.get('qos_policy_id', None))
|
||||||
|
|
||||||
LOG.info(_LI("DFMechDriver: create port %s"), port['id'])
|
LOG.info(_LI("DFMechDriver: create port %s"), port['id'])
|
||||||
return port
|
return port
|
||||||
|
@ -629,8 +654,8 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
[]),
|
[]),
|
||||||
binding_profile=updated_port.get(portbindings.PROFILE, None),
|
binding_profile=updated_port.get(portbindings.PROFILE, None),
|
||||||
binding_vnic_type=updated_port.get(portbindings.VNIC_TYPE, None),
|
binding_vnic_type=updated_port.get(portbindings.VNIC_TYPE, None),
|
||||||
version=updated_port['revision_number'],
|
version=updated_port['revision_number'], remote_vtep=remote_vtep,
|
||||||
remote_vtep=remote_vtep)
|
qos_policy_id=updated_port.get('qos_policy_id'))
|
||||||
|
|
||||||
LOG.info(_LI("DFMechDriver: update port %s"), updated_port['id'])
|
LOG.info(_LI("DFMechDriver: update port %s"), updated_port['id'])
|
||||||
return updated_port
|
return updated_port
|
||||||
|
@ -646,6 +671,7 @@ class DFMechDriver(driver_api.MechanismDriver):
|
||||||
except df_exceptions.DBKeyNotFound:
|
except df_exceptions.DBKeyNotFound:
|
||||||
LOG.debug("port %s is not found in DF DB, might have "
|
LOG.debug("port %s is not found in DF DB, might have "
|
||||||
"been deleted concurrently" % port_id)
|
"been deleted concurrently" % port_id)
|
||||||
|
return
|
||||||
|
|
||||||
LOG.info(_LI("DFMechDriver: delete port %s"), port_id)
|
LOG.info(_LI("DFMechDriver: delete port %s"), port_id)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
# 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 neutron import manager
|
||||||
|
from neutron.plugins.common import constants as service_constants
|
||||||
|
from neutron.plugins.ml2 import plugin as ml2_plugin
|
||||||
|
from neutron.services.qos.notification_drivers import qos_base
|
||||||
|
|
||||||
|
from dragonflow.db.neutron import lockedobjects_db as lock_db
|
||||||
|
|
||||||
|
|
||||||
|
class DFQosServiceNotificationDriver(
|
||||||
|
qos_base.QosServiceNotificationDriverBase):
|
||||||
|
"""Dragonflow notification driver for QoS."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._nb_api = None
|
||||||
|
|
||||||
|
def get_description(self):
|
||||||
|
return "Notification driver for Dragonflow"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def _plugin(self):
|
||||||
|
return manager.NeutronManager.get_service_plugins().get(
|
||||||
|
service_constants.QOS)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def nb_api(self):
|
||||||
|
if self._nb_api is None:
|
||||||
|
plugin = manager.NeutronManager.get_plugin()
|
||||||
|
if isinstance(plugin, ml2_plugin.Ml2Plugin):
|
||||||
|
mech_driver = plugin.mechanism_manager.mech_drivers['df'].obj
|
||||||
|
self._nb_api = mech_driver.nb_api
|
||||||
|
else:
|
||||||
|
# DF neutron plugin
|
||||||
|
self._nb_api = plugin.nb_api
|
||||||
|
return self._nb_api
|
||||||
|
|
||||||
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_QOS_POLICY_CREATE_OR_UPDATE)
|
||||||
|
def create_policy(self, context, policy):
|
||||||
|
self.nb_api.create_qos_policy(policy['id'],
|
||||||
|
policy['tenant_id'],
|
||||||
|
name=policy['name'],
|
||||||
|
rules=policy.get('rules', []),
|
||||||
|
version=policy['revision_number'])
|
||||||
|
|
||||||
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_QOS_POLICY_CREATE_OR_UPDATE)
|
||||||
|
def update_policy(self, context, policy):
|
||||||
|
policy_id = policy['id']
|
||||||
|
# NOTE: Neutron will not pass policy with latest revision_number
|
||||||
|
# in argument. Get the latest policy from neutron.
|
||||||
|
policy_neutron = self._plugin.get_policy(context, policy_id)
|
||||||
|
|
||||||
|
self.nb_api.update_qos_policy(
|
||||||
|
policy_id, policy_neutron['tenant_id'],
|
||||||
|
name=policy['name'], rules=policy_neutron['rules'],
|
||||||
|
version=policy_neutron['revision_number'])
|
||||||
|
|
||||||
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_QOS_POLICY_DELETE)
|
||||||
|
def delete_policy(self, context, policy):
|
||||||
|
policy_id = policy['id']
|
||||||
|
# Only id will be in policy in the argument. Get full policy from
|
||||||
|
# neutron.
|
||||||
|
policy_neutron = self._plugin.get_policy(context, policy_id)
|
||||||
|
|
||||||
|
self.nb_api.delete_qos_policy(policy_id, policy_neutron['tenant_id'])
|
|
@ -14,11 +14,15 @@ import contextlib
|
||||||
|
|
||||||
from neutronclient.common import exceptions as n_exc
|
from neutronclient.common import exceptions as n_exc
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from dragonflow.tests.common import utils
|
from dragonflow.tests.common import utils
|
||||||
from dragonflow.tests.fullstack import test_base
|
from dragonflow.tests.fullstack import test_base
|
||||||
from dragonflow.tests.fullstack import test_objects as objects
|
from dragonflow.tests.fullstack import test_objects as objects
|
||||||
|
|
||||||
|
# TODO(xiaohhui): This should be removed, once the DFPlugin has been removed.
|
||||||
|
DF_PLUGIN = 'dragonflow.neutron.plugin.DFPlugin'
|
||||||
|
|
||||||
|
|
||||||
class TestNeutronAPIandDB(test_base.DFTestBase):
|
class TestNeutronAPIandDB(test_base.DFTestBase):
|
||||||
|
|
||||||
|
@ -174,6 +178,75 @@ class TestNeutronAPIandDB(test_base.DFTestBase):
|
||||||
network.close()
|
network.close()
|
||||||
self.assertFalse(network.exists())
|
self.assertFalse(network.exists())
|
||||||
|
|
||||||
|
def test_create_port_with_qospolicy(self):
|
||||||
|
if cfg.CONF.core_plugin == DF_PLUGIN:
|
||||||
|
return
|
||||||
|
|
||||||
|
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
|
||||||
|
network_id = network.create()
|
||||||
|
self.assertTrue(network.exists())
|
||||||
|
|
||||||
|
qospolicy = self.store(objects.QosPolicyTestObj(self.neutron,
|
||||||
|
self.nb_api))
|
||||||
|
qos_policy_id = qospolicy.create()
|
||||||
|
self.assertTrue(qospolicy.exists())
|
||||||
|
|
||||||
|
port = self.store(objects.PortTestObj(self.neutron,
|
||||||
|
self.nb_api,
|
||||||
|
network_id))
|
||||||
|
port_param = {
|
||||||
|
'admin_state_up': True,
|
||||||
|
'name': 'port1',
|
||||||
|
'network_id': network_id,
|
||||||
|
'qos_policy_id': qos_policy_id
|
||||||
|
}
|
||||||
|
port.create(port_param)
|
||||||
|
self.assertTrue(port.exists())
|
||||||
|
self.assertEqual(qos_policy_id,
|
||||||
|
port.get_logical_port().get_qos_policy_id())
|
||||||
|
|
||||||
|
port.close()
|
||||||
|
self.assertFalse(port.exists())
|
||||||
|
network.close()
|
||||||
|
self.assertFalse(network.exists())
|
||||||
|
qospolicy.close()
|
||||||
|
self.assertFalse(qospolicy.exists())
|
||||||
|
|
||||||
|
def test_update_port_with_qospolicy(self):
|
||||||
|
if cfg.CONF.core_plugin == DF_PLUGIN:
|
||||||
|
return
|
||||||
|
|
||||||
|
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
|
||||||
|
network_id = network.create()
|
||||||
|
self.assertTrue(network.exists())
|
||||||
|
|
||||||
|
qospolicy = self.store(objects.QosPolicyTestObj(self.neutron,
|
||||||
|
self.nb_api))
|
||||||
|
qos_policy_id = qospolicy.create()
|
||||||
|
self.assertTrue(qospolicy.exists())
|
||||||
|
|
||||||
|
port = self.store(objects.PortTestObj(self.neutron,
|
||||||
|
self.nb_api,
|
||||||
|
network_id))
|
||||||
|
port.create()
|
||||||
|
self.assertTrue(port.exists())
|
||||||
|
|
||||||
|
port_param = {
|
||||||
|
'admin_state_up': True,
|
||||||
|
'name': 'port1',
|
||||||
|
'qos_policy_id': qos_policy_id
|
||||||
|
}
|
||||||
|
port.update(port_param)
|
||||||
|
self.assertEqual(qos_policy_id,
|
||||||
|
port.get_logical_port().get_qos_policy_id())
|
||||||
|
|
||||||
|
port.close()
|
||||||
|
self.assertFalse(port.exists())
|
||||||
|
network.close()
|
||||||
|
self.assertFalse(network.exists())
|
||||||
|
qospolicy.close()
|
||||||
|
self.assertFalse(qospolicy.exists())
|
||||||
|
|
||||||
def test_delete_router_interface_port(self):
|
def test_delete_router_interface_port(self):
|
||||||
router = self.store(objects.RouterTestObj(self.neutron, self.nb_api))
|
router = self.store(objects.RouterTestObj(self.neutron, self.nb_api))
|
||||||
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
|
network = self.store(objects.NetworkTestObj(self.neutron, self.nb_api))
|
||||||
|
@ -241,6 +314,20 @@ class TestNeutronAPIandDB(test_base.DFTestBase):
|
||||||
secgroup.close()
|
secgroup.close()
|
||||||
self.assertFalse(secgroup.exists())
|
self.assertFalse(secgroup.exists())
|
||||||
|
|
||||||
|
def test_create_delete_qos_policy(self):
|
||||||
|
if cfg.CONF.core_plugin == DF_PLUGIN:
|
||||||
|
return
|
||||||
|
|
||||||
|
qospolicy = self.store(
|
||||||
|
objects.QosPolicyTestObj(self.neutron, self.nb_api))
|
||||||
|
policy_id = qospolicy.create()
|
||||||
|
self.assertTrue(qospolicy.exists())
|
||||||
|
rule = {'max_kbps': '1000', 'max_burst_kbps': '100'}
|
||||||
|
qospolicy.create_rule(policy_id, rule)
|
||||||
|
self.assertTrue(qospolicy.exists())
|
||||||
|
qospolicy.close()
|
||||||
|
self.assertFalse(qospolicy.exists())
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _prepare_ext_net(self):
|
def _prepare_ext_net(self):
|
||||||
external_net = objects.find_first_network(self.neutron,
|
external_net = objects.find_first_network(self.neutron,
|
||||||
|
|
|
@ -13,10 +13,13 @@
|
||||||
import contextlib
|
import contextlib
|
||||||
|
|
||||||
from oslo_concurrency import lockutils
|
from oslo_concurrency import lockutils
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from dragonflow.tests.fullstack import test_base
|
from dragonflow.tests.fullstack import test_base
|
||||||
from dragonflow.tests.fullstack import test_objects as objects
|
from dragonflow.tests.fullstack import test_objects as objects
|
||||||
|
|
||||||
|
DF_PLUGIN = 'dragonflow.neutron.plugin.DFPlugin'
|
||||||
|
|
||||||
|
|
||||||
class TestObjectVersion(test_base.DFTestBase):
|
class TestObjectVersion(test_base.DFTestBase):
|
||||||
|
|
||||||
|
@ -127,6 +130,25 @@ class TestObjectVersion(test_base.DFTestBase):
|
||||||
secgroup.close()
|
secgroup.close()
|
||||||
self.assertFalse(secgroup.exists())
|
self.assertFalse(secgroup.exists())
|
||||||
|
|
||||||
|
def test_qospolicy_version(self):
|
||||||
|
if cfg.CONF.core_plugin == DF_PLUGIN:
|
||||||
|
return
|
||||||
|
|
||||||
|
qospolicy = self.store(objects.QosPolicyTestObj(self.neutron,
|
||||||
|
self.nb_api))
|
||||||
|
policy_id = qospolicy.create()
|
||||||
|
self.assertTrue(qospolicy.exists())
|
||||||
|
version = self.nb_api.get_qos_policy(policy_id).get_version()
|
||||||
|
|
||||||
|
rule = {'max_kbps': '1000', 'max_burst_kbps': '100'}
|
||||||
|
qospolicy.create_rule(policy_id, rule)
|
||||||
|
self.assertTrue(qospolicy.exists())
|
||||||
|
new_version = self.nb_api.get_qos_policy(policy_id).get_version()
|
||||||
|
self.assertGreater(new_version, version)
|
||||||
|
|
||||||
|
qospolicy.close()
|
||||||
|
self.assertFalse(qospolicy.exists())
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def _prepare_ext_net(self):
|
def _prepare_ext_net(self):
|
||||||
external_net = objects.find_first_network(self.neutron,
|
external_net = objects.find_first_network(self.neutron,
|
||||||
|
|
|
@ -444,3 +444,34 @@ class FloatingipTestObj(object):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
utils.wait_until_true(internal_predicate, timeout, sleep, exception)
|
utils.wait_until_true(internal_predicate, timeout, sleep, exception)
|
||||||
|
|
||||||
|
|
||||||
|
class QosPolicyTestObj(object):
|
||||||
|
def __init__(self, neutron, nb_api):
|
||||||
|
self.policy_id = None
|
||||||
|
self.neutron = neutron
|
||||||
|
self.nb_api = nb_api
|
||||||
|
self.closed = False
|
||||||
|
|
||||||
|
def create(self, qospolicy={'name': 'myqospolicy1'}):
|
||||||
|
new_qospolicy = self.neutron.create_qos_policy({'policy':
|
||||||
|
qospolicy})
|
||||||
|
self.policy_id = new_qospolicy['policy']['id']
|
||||||
|
return self.policy_id
|
||||||
|
|
||||||
|
def create_rule(self, policy_id, rule):
|
||||||
|
self.neutron.create_bandwidth_limit_rule(
|
||||||
|
policy_id, {'bandwidth_limit_rule': rule})
|
||||||
|
return
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.closed or self.policy_id is None:
|
||||||
|
return
|
||||||
|
self.neutron.delete_qos_policy(self.policy_id)
|
||||||
|
self.closed = True
|
||||||
|
|
||||||
|
def exists(self):
|
||||||
|
qospolicy = self.nb_api.get_qos_policy(self.policy_id)
|
||||||
|
if qospolicy:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
import mock
|
||||||
|
from neutron.conf.services import qos_driver_manager as driver_mgr_config
|
||||||
|
from neutron import manager
|
||||||
|
from neutron.objects.qos import rule
|
||||||
|
from neutron.plugins.ml2 import config as ml2_config
|
||||||
|
|
||||||
|
from dragonflow.tests.unit import test_mech_driver
|
||||||
|
|
||||||
|
|
||||||
|
class TestDFQosNotificationDriver(test_mech_driver.DFMechanismDriverTestCase):
|
||||||
|
|
||||||
|
"""Test case of df qos notification drvier"""
|
||||||
|
|
||||||
|
_extension_drivers = ['qos']
|
||||||
|
|
||||||
|
def get_additional_service_plugins(self):
|
||||||
|
p = super(TestDFQosNotificationDriver,
|
||||||
|
self).get_additional_service_plugins()
|
||||||
|
p.update({'qos_plugin_name': 'qos'})
|
||||||
|
return p
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
ml2_config.cfg.CONF.set_override('extension_drivers',
|
||||||
|
self._extension_drivers,
|
||||||
|
group='ml2')
|
||||||
|
driver_mgr_config.register_qos_plugin_opts(ml2_config.cfg.CONF)
|
||||||
|
ml2_config.cfg.CONF.set_override('notification_drivers',
|
||||||
|
['df_notification_driver'], 'qos')
|
||||||
|
super(TestDFQosNotificationDriver, self).setUp()
|
||||||
|
self.plugin = (manager.NeutronManager.
|
||||||
|
get_service_plugins()['QOS'])
|
||||||
|
self.driver = (
|
||||||
|
self.plugin.notification_driver_manager.notification_drivers[0])
|
||||||
|
|
||||||
|
def _test_create_policy(self):
|
||||||
|
qos_policy = {'policy': {'name': "policy1", 'tenant_id': "tenant1"}}
|
||||||
|
qos_obj = self.plugin.create_policy(self.context, qos_policy)
|
||||||
|
self.assertGreater(qos_obj['revision_number'], 0)
|
||||||
|
self.driver.nb_api.create_qos_policy.assert_called_with(
|
||||||
|
mock.ANY, 'tenant1', name='policy1',
|
||||||
|
rules=[], version=qos_obj['revision_number'])
|
||||||
|
return qos_obj
|
||||||
|
|
||||||
|
def test_create_policy(self):
|
||||||
|
self._test_create_policy()
|
||||||
|
|
||||||
|
def test_update_policy(self):
|
||||||
|
qos_obj = self._test_create_policy()
|
||||||
|
new_qos_obj = self.plugin.update_policy(
|
||||||
|
self.context, qos_obj['id'], {'policy': {'name': 'policy2'}})
|
||||||
|
self.assertGreater(new_qos_obj['revision_number'],
|
||||||
|
qos_obj['revision_number'])
|
||||||
|
self.driver.nb_api.update_qos_policy.assert_called_with(
|
||||||
|
qos_obj['id'], 'tenant1', name='policy2',
|
||||||
|
rules=[], version=new_qos_obj['revision_number'])
|
||||||
|
|
||||||
|
def test_create_delete_policy_rule(self):
|
||||||
|
qos_obj = self._test_create_policy()
|
||||||
|
qos_rule = {'max_burst_kbps': 1000,
|
||||||
|
'max_kbps': 100}
|
||||||
|
qos_rule_obj = self.plugin.create_policy_rule(
|
||||||
|
self.context, rule.QosBandwidthLimitRule,
|
||||||
|
qos_obj['id'], {'bandwidth_limit_rule': qos_rule})
|
||||||
|
new_qos_obj = self.plugin.get_policy(self.context, qos_obj['id'])
|
||||||
|
self.assertGreater(new_qos_obj['revision_number'],
|
||||||
|
qos_obj['revision_number'])
|
||||||
|
self.driver.nb_api.update_qos_policy.assert_called_with(
|
||||||
|
qos_obj['id'], 'tenant1', name='policy1',
|
||||||
|
rules=[qos_rule_obj], version=new_qos_obj['revision_number'])
|
||||||
|
|
||||||
|
self.plugin.delete_policy_rule(self.context,
|
||||||
|
rule.QosBandwidthLimitRule,
|
||||||
|
qos_rule_obj['id'],
|
||||||
|
qos_obj['id'])
|
||||||
|
newer_qos_obj = self.plugin.get_policy(self.context, qos_obj['id'])
|
||||||
|
self.assertGreater(newer_qos_obj['revision_number'],
|
||||||
|
new_qos_obj['revision_number'])
|
||||||
|
self.driver.nb_api.update_qos_policy.assert_called_with(
|
||||||
|
qos_obj['id'], 'tenant1', name='policy1',
|
||||||
|
rules=[], version=newer_qos_obj['revision_number'])
|
||||||
|
|
||||||
|
def test_delete_policy(self):
|
||||||
|
qos_obj = self._test_create_policy()
|
||||||
|
self.plugin.delete_policy(self.context, qos_obj['id'])
|
||||||
|
self.driver.nb_api.delete_qos_policy.assert_called_with(
|
||||||
|
qos_obj['id'], 'tenant1')
|
|
@ -123,7 +123,8 @@ class TestDFMechDriver(DFMechanismDriverTestCase):
|
||||||
physical_network=network['provider:physical_network'],
|
physical_network=network['provider:physical_network'],
|
||||||
router_external=network['router:external'],
|
router_external=network['router:external'],
|
||||||
mtu=network['mtu'], version=network['revision_number'],
|
mtu=network['mtu'], version=network['revision_number'],
|
||||||
subnets=[])
|
subnets=[],
|
||||||
|
qos_policy_id=None)
|
||||||
return network
|
return network
|
||||||
|
|
||||||
def test_create_network_revision(self):
|
def test_create_network_revision(self):
|
||||||
|
@ -221,7 +222,8 @@ class TestDFMechDriver(DFMechanismDriverTestCase):
|
||||||
remote_vtep=False,
|
remote_vtep=False,
|
||||||
allowed_address_pairs=mock.ANY,
|
allowed_address_pairs=mock.ANY,
|
||||||
binding_profile=mock.ANY,
|
binding_profile=mock.ANY,
|
||||||
binding_vnic_type=mock.ANY)
|
binding_vnic_type=mock.ANY,
|
||||||
|
qos_policy_id=None)
|
||||||
|
|
||||||
data = {'port': {'name': 'updated'}}
|
data = {'port': {'name': 'updated'}}
|
||||||
req = self.new_update_request('ports', data, port['id'])
|
req = self.new_update_request('ports', data, port['id'])
|
||||||
|
@ -244,7 +246,8 @@ class TestDFMechDriver(DFMechanismDriverTestCase):
|
||||||
port_security_enabled=mock.ANY,
|
port_security_enabled=mock.ANY,
|
||||||
allowed_address_pairs=mock.ANY,
|
allowed_address_pairs=mock.ANY,
|
||||||
binding_profile=mock.ANY,
|
binding_profile=mock.ANY,
|
||||||
binding_vnic_type=mock.ANY)
|
binding_vnic_type=mock.ANY,
|
||||||
|
qos_policy_id=None)
|
||||||
|
|
||||||
def test_delete_network(self):
|
def test_delete_network(self):
|
||||||
network = self._test_create_network_revision()
|
network = self._test_create_network_revision()
|
||||||
|
@ -272,6 +275,7 @@ class TestDFMechDriver(DFMechanismDriverTestCase):
|
||||||
device_id=port['device_id'],
|
device_id=port['device_id'],
|
||||||
security_groups=mock.ANY,
|
security_groups=mock.ANY,
|
||||||
port_security_enabled=mock.ANY,
|
port_security_enabled=mock.ANY,
|
||||||
|
qos_policy_id=mock.ANY,
|
||||||
remote_vtep=True,
|
remote_vtep=True,
|
||||||
allowed_address_pairs=mock.ANY,
|
allowed_address_pairs=mock.ANY,
|
||||||
binding_profile=profile,
|
binding_profile=profile,
|
||||||
|
@ -291,6 +295,7 @@ class TestDFMechDriver(DFMechanismDriverTestCase):
|
||||||
version=mock.ANY,
|
version=mock.ANY,
|
||||||
device_owner=port['device_owner'],
|
device_owner=port['device_owner'],
|
||||||
device_id=port['device_id'],
|
device_id=port['device_id'],
|
||||||
|
qos_policy_id=mock.ANY,
|
||||||
remote_vtep=True,
|
remote_vtep=True,
|
||||||
security_groups=mock.ANY,
|
security_groups=mock.ANY,
|
||||||
port_security_enabled=mock.ANY,
|
port_security_enabled=mock.ANY,
|
||||||
|
|
|
@ -48,6 +48,8 @@ output_file = dragonflow/locale/dragonflow.pot
|
||||||
|
|
||||||
|
|
||||||
[entry_points]
|
[entry_points]
|
||||||
|
neutron.qos.notification_drivers =
|
||||||
|
df_notification_driver = dragonflow.neutron.services.qos.notification_drivers.df_qos_notification_driver:DFQosServiceNotificationDriver
|
||||||
neutron.ml2.mechanism_drivers =
|
neutron.ml2.mechanism_drivers =
|
||||||
df = dragonflow.neutron.ml2.mech_driver:DFMechDriver
|
df = dragonflow.neutron.ml2.mech_driver:DFMechDriver
|
||||||
neutron.db.alembic_migrations =
|
neutron.db.alembic_migrations =
|
||||||
|
|
Loading…
Reference in New Issue