diff options
Diffstat (limited to 'networking-odl')
98 files changed, 1032 insertions, 3332 deletions
diff --git a/networking-odl/.gitignore b/networking-odl/.gitignore index 7287aff..104c929 100644 --- a/networking-odl/.gitignore +++ b/networking-odl/.gitignore @@ -8,15 +8,12 @@ dist/ doc/build *.DS_Store *.pyc -etc/neutron/plugins/ml2/ml2_conf_odl.ini.sample networking_odl.egg-info/ networking_odl/vcsversion.py networking_odl/versioninfo pbr*.egg/ run_tests.err.log run_tests.log -# Files create dy releasenotes build -releasenotes/build setuptools*.egg/ subunit.log *.mo diff --git a/networking-odl/.gitreview b/networking-odl/.gitreview index ad57c24..a3240f0 100644 --- a/networking-odl/.gitreview +++ b/networking-odl/.gitreview @@ -2,3 +2,4 @@ host=review.openstack.org port=29418 project=openstack/networking-odl.git +defaultbranch=stable/mitaka diff --git a/networking-odl/TESTING.rst b/networking-odl/TESTING.rst index d181286..109aeb3 100644 --- a/networking-odl/TESTING.rst +++ b/networking-odl/TESTING.rst @@ -101,19 +101,6 @@ see this wiki page: .. _tox: http://tox.readthedocs.org/en/latest/ .. _virtualenvs: https://pypi.python.org/pypi/virtualenv -Tests written can also be debugged by adding pdb break points. Normally if you add -a break point and just run the tests with normal flags they will end up in failing. -There is debug flag you can use to run after adding pdb break points in the tests. - -Set break points in your test code and run:: - - tox -e debug networking_odl.tests.unit.db.test_db.DbTestCase.test_validate_updates_same_object_uuid - -The package oslotest was used to enable debugging in the tests. For more -information see the link: - - http://docs.openstack.org/developer/oslotest/features.html - Running individual tests ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/networking-odl/devstack/README.rst b/networking-odl/devstack/README.rst index 2b4bd1c..ba6f716 100644 --- a/networking-odl/devstack/README.rst +++ b/networking-odl/devstack/README.rst @@ -16,23 +16,20 @@ [[local|localrc]] enable_plugin networking-odl http://git.openstack.org/openstack/networking-odl -4. Optionally, to enable support for OpenDaylight L3 router functionality, - add the below. - Note: This is only relevant when using old netvirt (ovsdb based, default):: +4. Optionally, to enable support for OpenDaylight L3 router functionality, add the + below:: > cat local.conf [[local|localrc]] ODL_L3=True 5. If you need to route the traffic out of the box (e.g. br-ex), set - ODL_PROVIDER_MAPPINGS to map the physical provider network to device - mapping, as shown below:: + ODL_PROVIDER_MAPPINGS to map the interface, as shown below:: > cat local.conf [[local|localrc]] ODL_L3=True - ODL_PROVIDER_MAPPINGS=${ODL_PROVIDER_MAPPINGS:-br-ex:eth2} # for old netvirt (ovsdb based) - ODL_PROVIDER_MAPPINGS=${ODL_PROVIDER_MAPPINGS:-physnet1:eth2} # for new netvirt (vpnservice based) + ODL_PROVIDER_MAPPINGS=${ODL_PROVIDER_MAPPINGS:-br-ex:eth2} 6. Optionally, to enable support for OpenDaylight with LBaaS V2, add this:: @@ -44,8 +41,8 @@ 7. run ``stack.sh`` -8. Note: In a multi-node devstack environment, for each compute node you will - want to add this to the local.conf file:: +8. Note: In a multi-node devstack environment, for each compute node you will want to add this + to the local.conf file:: > cat local.conf [[local|localrc]] @@ -54,8 +51,8 @@ 9. Note: In a node using a release of Open vSwitch provided from another source than your Linux distribution you have to enable in your local.conf skipping - of OVS installation step by setting *SKIP_OVS_INSTALL=True*. For example - when stacking together with `networking-ovs-dpdk + of OVS installation step by setting *SKIP_OVS_INSTALL=True*. For example when + stacking together with `networking-ovs-dpdk <https://github.com/openstack/networking-ovs-dpdk/>`_ Neutron plug-in to avoid conflicts between openvswitch and ovs-dpdk you have to add this to the local.conf file:: @@ -66,15 +63,3 @@ enable_plugin networking-odl http://git.openstack.org/openstack/networking-odl SKIP_OVS_INSTALL=True Q_ML2_PLUGIN_MECHANISM_DRIVERS=opendaylight - -10. Note: Optionally, to use the new netvirt implementation - (netvirt-vpnservice-openstack), add the following to the local.conf file - (only allinone topology is currently supported by devstack, since tunnel - endpoints are not automatically configured). For tunnel configurations - after loading devstack, please refer to this guide - https://wiki.opendaylight.org/view/Netvirt:_L2Gateway_HowTo#Configuring_Tunnels:: - - > cat local.conf - [[local|localrc]] - ODL_NETVIRT_KARAF_FEATURE=odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-netvirt-vpnservice-openstack - ODL_BOOT_WAIT_URL=restconf/operational/network-topology:network-topology/ # Workaround since netvirt:1 no longer exists in DS! diff --git a/networking-odl/devstack/entry_points b/networking-odl/devstack/entry_points index 647960f..939839a 100644 --- a/networking-odl/devstack/entry_points +++ b/networking-odl/devstack/entry_points @@ -54,9 +54,8 @@ function configure_opendaylight { fi fi - # Configure L3 if the user wants it for NETVIRT_OVSDB - # L3 is always enabled in NETVIRT_VPNSERVICE - if [[ ",$ODL_NETVIRT_KARAF_FEATURE," =~ ",$ODL_NETVIRT_KARAF_FEATURE_OVSDB," ]] && [ "${ODL_L3}" == "True" ]; then + # Configure L3 if the user wants it + if [ "${ODL_L3}" == "True" ]; then # Configure L3 FWD if it's not there local L3FWD=$(cat $ODL_DIR/$ODL_NAME/etc/custom.properties | \ grep ^ovsdb.l3.fwd.enabled) @@ -129,10 +128,6 @@ function configure_neutron_odl { populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl username=$ODL_USERNAME populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl password=$ODL_PASSWORD populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl port_binding_controller=$ODL_PORT_BINDING_CONTROLLER - # When it's not set, the default value is set by networking-odl - if [[ -n "$ODL_HOSTCONF_URI" ]]; then - populate_ml2_config /$Q_PLUGIN_CONF_FILE ml2_odl odl_hostconf_uri=$ODL_HOSTCONF_URI - fi } function configure_neutron_odl_lightweight_testing { @@ -254,6 +249,7 @@ function cleanup_opendaylight_compute { done } + # bind_opendaylight_controller() - set control manager to OVS function bind_opendaylight_controller { echo_summary "Initializing OpenDaylight" @@ -263,13 +259,9 @@ function bind_opendaylight_controller { read ovstbl <<< $(sudo ovs-vsctl get Open_vSwitch . _uuid) local ODL_MANAGERS_PARAM=() for manager in $(echo $ODL_OVS_MANAGERS | tr "," "\n"); do - # ovsdbd doesn't understand localhost. replace it to numerical ip address - ODL_MANAGERS_PARAM=( "${ODL_MANAGERS_PARAM[@]}" "tcp:${manager/localhost/127.0.0.1}:$ODL_MGR_PORT" ) + ODL_MANAGERS_PARAM=( "${ODL_MANAGERS_PARAM[@]}" "tcp:$manager:$ODL_MGR_PORT" ) done - # don't overwrite the already existing managers - local ODL_MANAGERS_OLD=$(sudo ovs-vsctl get-manager) - local ODL_MANAGERS=$(echo $ODL_MANAGERS_OLD ${ODL_MANAGERS_PARAM[@]} | tr ' ' '\n' | sort | uniq | tr '\n' ' ') - sudo ovs-vsctl set-manager ${ODL_MANAGERS} + sudo ovs-vsctl set-manager ${ODL_MANAGERS_PARAM[@]} if [[ -n "$ODL_PROVIDER_MAPPINGS" ]]; then sudo ovs-vsctl set Open_vSwitch $ovstbl \ other_config:provider_mappings=$ODL_PROVIDER_MAPPINGS @@ -277,6 +269,7 @@ function bind_opendaylight_controller { sudo ovs-vsctl set Open_vSwitch $ovstbl other_config:local_ip=$ODL_LOCAL_IP } + # unbind_opendaylight_controller() - disconnect controller from switch and clear bridges function unbind_opendaylight_controller { sudo ovs-vsctl del-manager diff --git a/networking-odl/devstack/functions b/networking-odl/devstack/functions index ebd14da..970828d 100644 --- a/networking-odl/devstack/functions +++ b/networking-odl/devstack/functions @@ -20,46 +20,48 @@ function odl_update_maven_metadata_xml { wget -O $MAVENMETAFILE ${NEXUSPATH}/${BUNDLEVERSION}/maven-metadata.xml } -function _odl_export_snapshot_url_pkg { +function odl_export_snapshot_url_pkg { local ODL_DIR=$1 local ODL_URL_PREFIX=$2 local BUNDLEVERSION=$3 local OFFLINE=$4 - local BUNDLE_TIMESTAMP=$5 local MAVENMETAFILE=$ODL_DIR/maven-metadata.xml - local NEXUSPATH="${ODL_URL_PREFIX}/${ODL_URL_SNAPSHOT_REPOSITORY_PATH}/org/opendaylight/integration/distribution-karaf" - - if [ "$BUNDLE_TIMESTAMP" == "latest" ]; then - odl_update_maven_metadata_xml $MAVENMETAFILE $NEXUSPATH $BUNDLEVERSION $OFFLINE - if is_ubuntu; then - install_package libxml-xpath-perl - BUNDLE_TIMESTAMP=`xpath -e "//snapshotVersion[extension='zip'][1]/value/text()" $MAVENMETAFILE 2>/dev/null` - else - yum_install perl-XML-XPath - BUNDLE_TIMESTAMP=`xpath $MAVENMETAFILE "//snapshotVersion[extension='zip'][1]/value/text()" 2>/dev/null` - fi + local NEXUSPATH="${ODL_URL_PREFIX}/content/repositories/opendaylight.snapshot/org/opendaylight/integration/distribution-karaf" + local BUNDLE_TIMESTAMP + + odl_update_maven_metadata_xml $MAVENMETAFILE $NEXUSPATH $BUNDLEVERSION $OFFLINE + if is_ubuntu; then + install_package libxml-xpath-perl + BUNDLE_TIMESTAMP=`xpath -e "//snapshotVersion[extension='zip'][1]/value/text()" $MAVENMETAFILE 2>/dev/null` + else + yum_install perl-XML-XPath + BUNDLE_TIMESTAMP=`xpath $MAVENMETAFILE "//snapshotVersion[extension='zip'][1]/value/text()" 2>/dev/null` fi export ODL_URL=${NEXUSPATH}/${BUNDLEVERSION} export ODL_PKG=distribution-karaf-${BUNDLE_TIMESTAMP}.zip + + # The network virtualization feature used by opendaylight loaded by Karaf + ODL_NETVIRT_KARAF_FEATURE=${ODL_NETVIRT_KARAF_FEATURE:-odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-ovsdb-openstack} + + # The url that this version of ODL netvirt can use to know ODL is fully up + export ODL_BOOT_WAIT_URL=${ODL_BOOT_WAIT_URL:-restconf/operational/network-topology:network-topology/topology/netvirt:1} } -function _odl_export_release_url_pkg { +function odl_export_release_url_pkg { local ODL_URL_PREFIX=$1 local BUNDLEVERSION=$2 - local NEXUSPATH="${ODL_URL_PREFIX}/${ODL_URL_RELEASE_REPOSITORY_PATH}/org/opendaylight/integration/distribution-karaf" + local NEXUSPATH="${ODL_URL_PREFIX}/content/repositories/opendaylight.release/org/opendaylight/integration/distribution-karaf" export ODL_URL=${NEXUSPATH}/${BUNDLEVERSION} export ODL_PKG=distribution-karaf-${BUNDLEVERSION}.zip -} -function setup_opendaylight_package { - if [[ -n "$ODL_SNAPSHOT_VERSION" ]]; then - _odl_export_snapshot_url_pkg ${ODL_DIR} ${ODL_URL_PREFIX} ${ODL_BUNDLEVERSION} ${OFFLINE} ${ODL_SNAPSHOT_VERSION} - else - _odl_export_release_url_pkg ${ODL_URL_PREFIX} ${ODL_BUNDLEVERSION} - fi + # The network virtualization feature used by opendaylight loaded by Karaf + ODL_NETVIRT_KARAF_FEATURE=${ODL_NETVIRT_KARAF_FEATURE:-odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-ovsdb-openstack} + + # The url that this version of ODL netvirt can use to know ODL is fully up + export ODL_BOOT_WAIT_URL=${ODL_BOOT_WAIT_URL:-restconf/operational/network-topology:network-topology/topology/netvirt:1} } # Test if OpenDaylight is enabled diff --git a/networking-odl/devstack/odl-releases/beryllium-0.4.0 b/networking-odl/devstack/odl-releases/beryllium-0.4.0 index 4ed5b73..b7f996a 100644 --- a/networking-odl/devstack/odl-releases/beryllium-0.4.0 +++ b/networking-odl/devstack/odl-releases/beryllium-0.4.0 @@ -1,4 +1,9 @@ -export ODL_BUNDLEVERSION='0.4.0-Beryllium' +export BUNDLEVERSION='0.4.0-Beryllium' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} + +function setup_opendaylight_package { + odl_export_release_url_pkg ${ODL_URL_PREFIX} ${BUNDLEVERSION} +} diff --git a/networking-odl/devstack/odl-releases/beryllium-0.4.1-SR1 b/networking-odl/devstack/odl-releases/beryllium-0.4.1-SR1 index f659a38..b71dd0a 100644 --- a/networking-odl/devstack/odl-releases/beryllium-0.4.1-SR1 +++ b/networking-odl/devstack/odl-releases/beryllium-0.4.1-SR1 @@ -1,4 +1,9 @@ -export ODL_BUNDLEVERSION='0.4.1-Beryllium-SR1' +export BUNDLEVERSION='0.4.1-Beryllium-SR1' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} + +function setup_opendaylight_package { + odl_export_release_url_pkg ${ODL_URL_PREFIX} ${BUNDLEVERSION} +} diff --git a/networking-odl/devstack/odl-releases/beryllium-0.4.2-SR2 b/networking-odl/devstack/odl-releases/beryllium-0.4.2-SR2 deleted file mode 100644 index 230682b..0000000 --- a/networking-odl/devstack/odl-releases/beryllium-0.4.2-SR2 +++ /dev/null @@ -1,4 +0,0 @@ -export ODL_BUNDLEVERSION='0.4.2-Beryllium-SR2' - -# Java major version required to run OpenDaylight: 7, 8, ... -ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} diff --git a/networking-odl/devstack/odl-releases/beryllium-0.4.3-SR3 b/networking-odl/devstack/odl-releases/beryllium-0.4.3-SR3 deleted file mode 100644 index 2edebc0..0000000 --- a/networking-odl/devstack/odl-releases/beryllium-0.4.3-SR3 +++ /dev/null @@ -1,4 +0,0 @@ -export ODL_BUNDLEVERSION='0.4.3-Beryllium-SR3' - -# Java major version required to run OpenDaylight: 7, 8, ... -ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} diff --git a/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.2 b/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.2 new file mode 100644 index 0000000..9d2d823 --- /dev/null +++ b/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.2 @@ -0,0 +1,9 @@ +BUNDLEVERSION='0.4.2-SNAPSHOT' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} + +# Java major version required to run OpenDaylight: 7, 8, ... +ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} + +function setup_opendaylight_package { + odl_export_snapshot_url_pkg ${ODL_DIR} ${ODL_URL_PREFIX} ${BUNDLEVERSION} ${OFFLINE} +} diff --git a/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.3 b/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.3 deleted file mode 100644 index a0d8ecc..0000000 --- a/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.3 +++ /dev/null @@ -1,5 +0,0 @@ -ODL_BUNDLEVERSION='0.4.3-SNAPSHOT' -ODL_SNAPSHOT_VERSION=${ODL_SNAPSHOT_VERSION:-latest} - -# Java major version required to run OpenDaylight: 7, 8, ... -ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} diff --git a/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.4 b/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.4 deleted file mode 100644 index 3ea7fac..0000000 --- a/networking-odl/devstack/odl-releases/beryllium-snapshot-0.4.4 +++ /dev/null @@ -1,5 +0,0 @@ -ODL_BUNDLEVERSION='0.4.4-SNAPSHOT' -ODL_SNAPSHOT_VERSION=${ODL_SNAPSHOT_VERSION:-latest} - -# Java major version required to run OpenDaylight: 7, 8, ... -ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} diff --git a/networking-odl/devstack/odl-releases/boron-snapshot-0.5.0 b/networking-odl/devstack/odl-releases/boron-snapshot-0.5.0 index 1afdb92..31a042a 100644 --- a/networking-odl/devstack/odl-releases/boron-snapshot-0.5.0 +++ b/networking-odl/devstack/odl-releases/boron-snapshot-0.5.0 @@ -1,5 +1,9 @@ -ODL_BUNDLEVERSION='0.5.0-SNAPSHOT' -ODL_SNAPSHOT_VERSION=${ODL_SNAPSHOT_VERSION:-latest} +BUNDLEVERSION='0.5.0-SNAPSHOT' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} + +function setup_opendaylight_package { + odl_export_snapshot_url_pkg ${ODL_DIR} ${ODL_URL_PREFIX} ${BUNDLEVERSION} ${OFFLINE} +} diff --git a/networking-odl/devstack/odl-releases/carbon-snapshot-0.6.0 b/networking-odl/devstack/odl-releases/carbon-snapshot-0.6.0 deleted file mode 100644 index 102de66..0000000 --- a/networking-odl/devstack/odl-releases/carbon-snapshot-0.6.0 +++ /dev/null @@ -1,5 +0,0 @@ -ODL_BUNDLEVERSION='0.6.0-SNAPSHOT' -ODL_SNAPSHOT_VERSION=${ODL_SNAPSHOT_VERSION:-latest} - -# Java major version required to run OpenDaylight: 7, 8, ... -ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-8} diff --git a/networking-odl/devstack/odl-releases/common b/networking-odl/devstack/odl-releases/common deleted file mode 100644 index ec57dd9..0000000 --- a/networking-odl/devstack/odl-releases/common +++ /dev/null @@ -1,14 +0,0 @@ -# karaf distribution name of ODL to download -export ODL_NAME=${ODL_NAME:-distribution-karaf-${ODL_BUNDLEVERSION}} - -# The network virtualization older feature name (ovsdb based) -export ODL_NETVIRT_KARAF_FEATURE_OVSDB=${ODL_NETVIRT_KARAF_FEATURE_OVSDB:-odl-ovsdb-openstack} - -# The network virtualization newer feature name (vpnservice based) -export ODL_NETVIRT_KARAF_FEATURE_VPNSERVICE=${ODL_NETVIRT_KARAF_FEATURE_VPNSERVICE:-odl-netvirt-openstack} - -# The network virtualization feature used by opendaylight loaded by Karaf -export ODL_NETVIRT_KARAF_FEATURE=${ODL_NETVIRT_KARAF_FEATURE:-odl-neutron-service,odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,$ODL_NETVIRT_KARAF_FEATURE_OVSDB} - -# The url that this version of ODL netvirt can use to know ODL is fully up -export ODL_BOOT_WAIT_URL=${ODL_BOOT_WAIT_URL:-restconf/operational/network-topology:network-topology/topology/netvirt:1} diff --git a/networking-odl/devstack/odl-releases/helium-0.2.3-SR3 b/networking-odl/devstack/odl-releases/helium-0.2.3-SR3 index 70149e9..ca51cf2 100644 --- a/networking-odl/devstack/odl-releases/helium-0.2.3-SR3 +++ b/networking-odl/devstack/odl-releases/helium-0.2.3-SR3 @@ -4,14 +4,13 @@ export ODL_NAME=distribution-karaf-0.2.3-Helium-SR3 # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-7} -# overwrite this function function setup_opendaylight_package { # The OpenDaylight URL export ODL_URL=${ODL_URL_PREFIX}/content/repositories/public/org/opendaylight/integration/distribution-karaf/0.2.3-Helium-SR3 # The OpenDaylight Package export ODL_PKG=distribution-karaf-0.2.3-Helium-SR3.zip -} -# The network virtualization feature used by opendaylight loaded by Karaf -ODL_NETVIRT_KARAF_FEATURE=${ODL_NETVIRT_KARAF_FEATURE:-odl-base-all,odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-adsal-northbound,odl-nsf-all,odl-ovsdb-northbound,odl-ovsdb-openstack} + # The network virtualization feature used by opendaylight loaded by Karaf + ODL_NETVIRT_KARAF_FEATURE=${ODL_NETVIRT_KARAF_FEATURE:-odl-base-all,odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-adsal-northbound,odl-nsf-all,odl-ovsdb-northbound,odl-ovsdb-openstack} +} diff --git a/networking-odl/devstack/odl-releases/lithium-0.3.1-SR1 b/networking-odl/devstack/odl-releases/lithium-0.3.1-SR1 index 81283a7..ff23095 100644 --- a/networking-odl/devstack/odl-releases/lithium-0.3.1-SR1 +++ b/networking-odl/devstack/odl-releases/lithium-0.3.1-SR1 @@ -1,4 +1,9 @@ -export ODL_BUNDLEVERSION='0.3.1-Lithium-SR1' +export BUNDLEVERSION='0.3.1-Lithium-SR1' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-7} + +function setup_opendaylight_package { + odl_export_release_url_pkg ${ODL_URL_PREFIX} ${BUNDLEVERSION} +} diff --git a/networking-odl/devstack/odl-releases/lithium-0.3.2-SR2 b/networking-odl/devstack/odl-releases/lithium-0.3.2-SR2 index ff52cad..d603409 100644 --- a/networking-odl/devstack/odl-releases/lithium-0.3.2-SR2 +++ b/networking-odl/devstack/odl-releases/lithium-0.3.2-SR2 @@ -1,4 +1,9 @@ -export ODL_BUNDLEVERSION='0.3.2-Lithium-SR2' +export BUNDLEVERSION='0.3.2-Lithium-SR2' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-7} + +function setup_opendaylight_package { + odl_export_release_url_pkg ${ODL_URL_PREFIX} ${BUNDLEVERSION} +} diff --git a/networking-odl/devstack/odl-releases/lithium-0.3.3-SR3 b/networking-odl/devstack/odl-releases/lithium-0.3.3-SR3 index a1ff407..2480d07 100644 --- a/networking-odl/devstack/odl-releases/lithium-0.3.3-SR3 +++ b/networking-odl/devstack/odl-releases/lithium-0.3.3-SR3 @@ -1,4 +1,9 @@ -export ODL_BUNDLEVERSION='0.3.3-Lithium-SR3' +export BUNDLEVERSION='0.3.3-Lithium-SR3' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-7} + +function setup_opendaylight_package { + odl_export_release_url_pkg ${ODL_URL_PREFIX} ${BUNDLEVERSION} +} diff --git a/networking-odl/devstack/odl-releases/lithium-0.3.4-SR4 b/networking-odl/devstack/odl-releases/lithium-0.3.4-SR4 index da2f692..6dc5cd6 100644 --- a/networking-odl/devstack/odl-releases/lithium-0.3.4-SR4 +++ b/networking-odl/devstack/odl-releases/lithium-0.3.4-SR4 @@ -1,4 +1,9 @@ -export ODL_BUNDLEVERSION='0.3.4-Lithium-SR4' +export BUNDLEVERSION='0.3.4-Lithium-SR4' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} # Java major version required to run OpenDaylight: 7, 8, ... ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-7} + +function setup_opendaylight_package { + odl_export_release_url_pkg ${ODL_URL_PREFIX} ${BUNDLEVERSION} +} diff --git a/networking-odl/devstack/odl-releases/lithium-snapshot-0.3.5 b/networking-odl/devstack/odl-releases/lithium-snapshot-0.3.5 new file mode 100644 index 0000000..73aa50f --- /dev/null +++ b/networking-odl/devstack/odl-releases/lithium-snapshot-0.3.5 @@ -0,0 +1,9 @@ +BUNDLEVERSION='0.3.5-SNAPSHOT' +export ODL_NAME=distribution-karaf-${BUNDLEVERSION} + +# Java major version required to run OpenDaylight: 7, 8, ... +ODL_REQUIRED_JAVA_VERSION=${ODL_REQUIRED_JAVA_VERSION:-7} + +function setup_opendaylight_package { + odl_export_snapshot_url_pkg ${ODL_DIR} ${ODL_URL_PREFIX} ${BUNDLEVERSION} ${OFFLINE} +} diff --git a/networking-odl/devstack/override-defaults b/networking-odl/devstack/override-defaults index 399a528..82ec60e 100644 --- a/networking-odl/devstack/override-defaults +++ b/networking-odl/devstack/override-defaults @@ -3,14 +3,7 @@ # We will enable the opendaylight ML2 MechanismDriver v1 version by default. # Note we are also enabling the logger driver, which is helpful for # debugging things on the Neutron side. -if [[ "$ODL_V2DRIVER" == "True" ]] -then - V2_POSTFIX="_v2" -else - V2_POSTFIX="" -fi - -Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-"opendaylight${V2_POSTFIX},logger"} +Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-"opendaylight,logger"} # This triggers the provisioning of L3 resources like routers and # external network, if not overriden. @@ -23,11 +16,10 @@ disable_service q-agt # If ODL_L3 is enabled, then we don't need the L3 agent and OpenDaylight # is going to act as the ML2's L3 service plugin. -# NETVIRT_VPNSERVICE feature enables ODL L3 by default, so ODL_L3 is disregarded. -if [[ ",$ODL_NETVIRT_KARAF_FEATURE," =~ ",$ODL_NETVIRT_KARAF_FEATURE_VPNSERVICE," ]] || [ "$ODL_L3" == "True" ]; +if [[ "$ODL_L3" == "True" ]] then disable_service q-l3 - ML2_L3_PLUGIN="${ML2_L3_PLUGIN:-odl-router${V2_POSTFIX}}" + ML2_L3_PLUGIN="${ML2_L3_PLUGIN:-odl-router}" fi # [networking-feature-enabled] api-extensions diff --git a/networking-odl/devstack/plugin.sh b/networking-odl/devstack/plugin.sh index a65840d..1f76b0f 100644 --- a/networking-odl/devstack/plugin.sh +++ b/networking-odl/devstack/plugin.sh @@ -24,7 +24,6 @@ source $TOP_DIR/lib/neutron_plugins/ovs_base # Import ODL settings source $NETWORKING_ODL_DIR/devstack/settings.odl source $NETWORKING_ODL_DIR/devstack/odl-releases/$ODL_RELEASE -source $NETWORKING_ODL_DIR/devstack/odl-releases/common # Utilities functions for setting up Java source $NETWORKING_ODL_DIR/devstack/setup_java.sh @@ -78,10 +77,7 @@ if is_service_enabled odl-compute; then fi bind_opendaylight_controller wait_for_active_bridge $OVS_BR $ODL_RETRY_SLEEP_INTERVAL $ODL_BOOT_WAIT - - # L3 needs to be configured only for netvirt-ovsdb - in netvirt-vpnservice L3 is configured - # by provider_mappings, and the provider mappings are added to br-int by default - if [[ ",$ODL_NETVIRT_KARAF_FEATURE," =~ ",$ODL_NETVIRT_KARAF_FEATURE_OVSDB," ]] && [ "${ODL_L3}" == "True" ]; then + if [ "${ODL_L3}" == "True" ]; then configure_opendaylight_l3 fi elif [[ "$1" == "stack" && "$2" == "extra" ]]; then diff --git a/networking-odl/devstack/pre_test_hook.sh b/networking-odl/devstack/pre_test_hook.sh index df11e2a..fbb7f7e 100644 --- a/networking-odl/devstack/pre_test_hook.sh +++ b/networking-odl/devstack/pre_test_hook.sh @@ -7,14 +7,14 @@ GATE_DEST=$BASE/new DEVSTACK_PATH=$GATE_DEST/devstack case "$ODL_RELEASE_BASE" in - carbon-snapshot) - ODL_RELEASE=carbon-snapshot-0.6.0 - ;; boron-snapshot) ODL_RELEASE=boron-snapshot-0.5.0 ;; beryllium-snapshot) - ODL_RELEASE=beryllium-snapshot-0.4.4 + ODL_RELEASE=beryllium-snapshot-0.4.2 + ;; + lithium-snapshot) + ODL_RELEASE=lithium-snapshot-0.3.5 ;; *) echo "Unknown ODL release base: $ODL_RELEASE_BASE" @@ -22,36 +22,6 @@ case "$ODL_RELEASE_BASE" in ;; esac -case "$ODL_GATE_V2DRIVER" in - v2driver) - ODL_V2DRIVER=True - ;; - v1driver|*) - ODL_V2DRIVER=False - ;; -esac - -case "$ODL_GATE_PORT_BINDING" in - pseudo-agentdb-binding) - ODL_PORT_BINDING_CONTROLLER=pseudo-agentdb-binding - ;; - legacy-port-binding) - ODL_PORT_BINDING_CONTROLLER=legacy-port-binding - ;; - network-topology|*) - ODL_PORT_BINDING_CONTROLLER=network-topology - ;; -esac - -case "$ODL_GATE_SERVICE_PROVIDER" in - vpnservice) - ODL_NETVIRT_KARAF_FEATURE=odl-neutron-service,odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-netvirt-openstack - ;; - netvirt|*) - ODL_NETVIRT_KARAF_FEATURE=odl-neutron-service,odl-restconf-all,odl-aaa-authn,odl-dlux-core,odl-mdsal-apidocs,odl-ovsdb-openstack - ;; -esac - cat <<EOF >> $DEVSTACK_PATH/localrc IS_GATE=True @@ -59,15 +29,6 @@ IS_GATE=True # Set here the ODL release to use for the Gate job ODL_RELEASE=${ODL_RELEASE} -# Set here which driver, v1 or v2 driver -ODL_V2DRIVER=${ODL_V2DRIVER} - -# Set here which port binding controller -ODL_PORT_BINDING_CONTROLLER=${ODL_PORT_BINDING_CONTROLLER} - -# Set here which ODL openstack service provider to use -ODL_NETVIRT_KARAF_FEATURE=${ODL_NETVIRT_KARAF_FEATURE} - # Switch to using the ODL's L3 implementation ODL_L3=True diff --git a/networking-odl/devstack/settings.odl b/networking-odl/devstack/settings.odl index 1ce23a4..5a1040b 100644 --- a/networking-odl/devstack/settings.odl +++ b/networking-odl/devstack/settings.odl @@ -3,21 +3,18 @@ # What release to use. Choices are: # -# carbon-snapshot-0.6.0 (master) -# boron-snapshot-0.5.0 (stable/boron) -# beryllium-snapshot-0.4.4 (stable/beryllium) -# beryllium-snapshot-0.4.3 (stable/beryllium) -# beryllium-0.4.3-SR3 -# beryllium-0.4.2-SR2 +# boron-snapshot-0.5.0 (master) +# beryllium-snapshot-0.4.2 (stable/beryllium) # beryllium-0.4.1-SR1 # beryllium-0.4.0 +# lithium-snapshot-0.3.5 (stable/lithium) # lithium-0.3.4-SR4 (SR4) # lithium-0.3.3-SR3 (SR3) # lithium-0.3.2-SR2 (SR2) # lithium-0.3.1-SR1 (SR1) # helium-0.2.3-SR3 -ODL_RELEASE=${ODL_RELEASE:-boron-snapshot-0.5.0} +ODL_RELEASE=${ODL_RELEASE:-beryllium-snapshot-0.4.2} # The IP address of ODL. Set this in local.conf. ODL_MGR_IP=${ODL_MGR_IP:-$SERVICE_HOST} @@ -45,18 +42,9 @@ ODL_USERNAME=${ODL_USERNAME:-admin} # The ODL password ODL_PASSWORD=${ODL_PASSWORD:-admin} -# use v2 type driver -# this requires post mitaka -ODL_V2DRIVER=${ODL_V2DRIVER:-False} - # The OpenDaylight URL PREFIX ODL_URL_PREFIX=${ODL_URL_PREFIX:-https://nexus.opendaylight.org} -# OpenDaylight snapshot & release repositories paths -# Can be overidden in case you host proxy repositories which have a different diretory structure than OpenDaylight's -ODL_URL_SNAPSHOT_REPOSITORY_PATH=${ODL_URL_SNAPSHOT_REPOSITORY_PATH:-content/repositories/opendaylight.snapshot} -ODL_URL_RELEASE_REPOSITORY_PATH=${ODL_URL_RELEASE_REPOSITORY_PATH:-content/repositories/opendaylight.release} - # How long (in seconds) to pause after ODL starts to let it complete booting ODL_BOOT_WAIT=${ODL_BOOT_WAIT:-600} @@ -102,15 +90,5 @@ ODL_RETRY_SLEEP_INTERVAL=${ODL_RETRY_SLEEP_INTERVAL:-5} # Skip installation of distribution provided Open vSwitch SKIP_OVS_INSTALL=$(trueorfalse False SKIP_OVS_INSTALL) -# The ODL Restconf URL -# URI to hostconfigs: empty for default value -ODL_HOSTCONF_URI=${ODL_HOSTCONF_URI:-} - # Port binding controller ODL_PORT_BINDING_CONTROLLER=${ODL_PORT_BINDING_CONTROLLER:-network-topology} - -# Snapshot version - allows using a specific version e.g. 0.5.0-20160719.101233-3643 -# latest: check the latest snapshot -# specific version: the specific version of the snapshot -# "": odl release -ODL_SNAPSHOT_VERSION=${ODL_SNAPSHOT_VERSION:-} diff --git a/networking-odl/doc/source/devref/hostconfig.rst b/networking-odl/doc/source/devref/hostconfig.rst deleted file mode 100644 index 70c8607..0000000 --- a/networking-odl/doc/source/devref/hostconfig.rst +++ /dev/null @@ -1,119 +0,0 @@ -Host Configuration -================== - -Overview --------- - -ODL is agentless configuration. In this scenario Host Configuration is used -to specify the physical host type and other configurations for the host -system. This information is populated by the Cloud Operator is in OVSDB in -Open_vSwitch configuration data in the external_ids field as a key value pair. -This information is then read by ODL and made available to networking-odl -through REST API. Networking-odl populates this information in agent_db in -Neutron and is then used by Neutron scheduler. This information is required -for features like Port binding and Router scheduling. - -Refer to this link for detailed design for this feature. - -https://docs.google.com/presentation/d/1kq0elysCDEmIWs3omTi5RoXTSBbrewn11Je2d26cI4M/edit?pref=2&pli=1#slide=id.g108988d1e3_0_6 - -Related ODL changes: - -https://git.opendaylight.org/gerrit/#/c/36767/ - -https://git.opendaylight.org/gerrit/#/c/40143/ - -Host Configuration fields -------------------------- - -- host-id - -This represents host identification string. This string will be stored in -external_ids field with the key as odl_os_hostconfig_hostid. -Refer to Neutron config definition for host field for details on this field. - -http://docs.openstack.org/kilo/config-reference/content/section_neutron.conf.html - -- host-type - -The field is for type of the node. This value corresponds to agent_type in -agent_db. Example value are “ODL L2” and “ODL L3” for Compute and Network node -respectively. Same host can be configured to have multiple configurations and -can therefore can have both L2, L3 and other configurations at the same time. -This string will be populated by ODL based on the configurations available -on the host. See example in section below. - -- config - -This is the configuration data for the host type. Since same node can be -configured to store multiple configurations different external_ids key value -pair are used to store these configuration. The external_ids with keys as -odl_os_hostconfig_config_odl_XXXXXXXX store different configurations. -8 characters after the suffix odl_os_hostconfig_config_odl are host type. -ODL extracts these characters and store that as the host-type fields. For -example odl_os_hostconfig_config_odl_l2, odl_os_hostconfig_config_odl_l3 keys -are used to provide L2 and L3 configurations respectively. ODL will extract -"ODL L2" and "ODL L3" as host-type field from these keys and populate -host-type field. - -Config is a Json string. Some examples of config: - -:: - - {“supported_vnic_types”: [{ - “vnic_type”: “normal”, - “vif_type”: “ovs”, - “vif_details”: “{}” - }] - “allowed_network_types”: ["local", "gre", "vlan", "vxlan"]”, - “bridge_mappings”: {“physnet1":"br-ex”} - }" - - {“supported_vnic_types”: [{ - “vnic_type”: “normal”, - “vif_type”: “vhostuser”, - “vif_details”: “{“port_filter”: “False”, “vhostuser_socket”: “/var/run/openvswitch”}” - }] - “allowed_network_types”: ["local", "gre", "vlan", "vxlan"]”, - “bridge_mappings”: {“physnet1":"br-ex”} - }" - -**Host Config URL** - -Url : http://ip:odlport/restconf/operational/neutron:neutron/hostconfigs/ - -**Commands to setup host config in OVSDB** -:: - - export OVSUUID=$(ovs-vsctl get Open_vSwitch . _uuid) - ovs-vsctl set Open_vSwitch $OVSUUID external_ids:odl_os_hostconfig_hostid=test_host - ovs-vsctl set Open_vSwitch $OVSUUID external_ids:odl_os_hostconfig_config_odl_l2 = - "{“supported_vnic_types”: [{“vnic_type”: “normal”, “vif_type”: “ovs”, "vif_details": {} }], “allowed_network_types”: [“local”], “bridge_mappings”: {“physnet1":"br-ex”}}" - -Example for host configuration -------------------------------- - -:: - - { - "hostconfigs": { - "hostconfig": [ - { - "host-id": "test_host1", - "host-type": "ODL L2", - "config": - "{“supported_vnic_types”: [{ - “vnic_type”: “normal”, - “vif_type”: “ovs”, - “vif_details”: {} - }] - “allowed_network_types”: ["local", "gre", "vlan", "vxlan"], - “bridge_mappings”: {“physnet1":"br-ex”}}" - }, - { - "host-id": "test_host2", - "host-type": "ODL L3", - "config": {} - }] - } - } diff --git a/networking-odl/doc/source/devref/index.rst b/networking-odl/doc/source/devref/index.rst deleted file mode 100644 index 1bf7790..0000000 --- a/networking-odl/doc/source/devref/index.rst +++ /dev/null @@ -1,36 +0,0 @@ -.. - 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. - - -Developer Guide -=============== - -In the Developer Guide, you will find information on networking-odl's lower -level design and implementation details. - - -Contents: --------------------------------- -.. toctree:: - :maxdepth: 2 - - hostconfig - - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` - diff --git a/networking-odl/doc/source/features.rst b/networking-odl/doc/source/features.rst new file mode 100644 index 0000000..87e7027 --- /dev/null +++ b/networking-odl/doc/source/features.rst @@ -0,0 +1,9 @@ +======== +Features +======== + +.. toctree:: + :maxdepth: 1 + + sfc-driver + diff --git a/networking-odl/doc/source/index.rst b/networking-odl/doc/source/index.rst index 312dbe0..05cd1d5 100644 --- a/networking-odl/doc/source/index.rst +++ b/networking-odl/doc/source/index.rst @@ -15,16 +15,7 @@ Contents: installation usage contributing - specs - -Developer Docs -============== - -.. toctree:: - :maxdepth: 1 - - devref/index - + features Indices and tables ================== diff --git a/networking-odl/doc/source/sfc-driver.rst b/networking-odl/doc/source/sfc-driver.rst new file mode 100644 index 0000000..a308656 --- /dev/null +++ b/networking-odl/doc/source/sfc-driver.rst @@ -0,0 +1,128 @@ +================================================= +Service Function Chaining Driver for OpenDaylight +================================================= + +This spec describes the plan to implement OpenStack networking-sfc[1] driver for OpenDaylight +Controller. + +Problem Statement +=================== +OpenStack SFC project (networking-sfc [1]) exposes generic APIs[2] for Service Function Chaining +(SFC) that can be implemented by any backend networking service provider to support SFC. These APIs +provide a way to integrate OpenStack SFC with any of the backend SFC providers. OpenDaylight SFC +project provides a very mature implementation of SFC [3], but currently there is no formal +integration mechanism present to consume OpenDaylight as an SFC provider for networking-sfc. + +Recently Tacker project [4] has been approved as an official project in OpenStack, that opens many +possibilities to realize the NFV use cases (e.g SFC) using OpenStack as a platform. Providing a +formal end to end integration between OpenStack and OpenDaylight for SFC use case will help NFV +users leverage OpenStack, Tacker and OpenDaylight as a solution. A POC for this integration work has +already been implemented [5][6] by Tim Rozet, but in this POC work, Tacker directly communicates +to OpenDaylight SFC & classifier providers and not through OpenStack SFC APIs (networking-sfc). + +Proposed Change +=============== +Implementation of this spec will introduce a networking-sfc[1] driver for OpenDaylight Controller in +networking-odl project that will pass through the networking-sfc API's call to the OpenDaylight +Controller. + +Detailed Design +=============== +To enable the formal end to end integration between OpenStack SFC and OpenDaylight requires an +SFC driver for OpenDaylight. ODL SFC driver will act as a shim layer between OpenStack and +OpenDaylight that will carry out following two main tasks: + +* Translation of OpenStack SFC Classifier API to ODL SFC classifier yang models** + +* Translation of OpenStack SFC API's to OpenDaylight Neutron Northbound SFC models** [8] + +** This work is not yet done, but the OpenDaylight neutron northbound project needs to come up with +yang models for SFC classification/chain. These models will be based on the existing networking-sfc +APIs. This work is out of scope of networking-odl work and will be collaborated in the scope of the +OpenDaylight Neutron Northbound project. + +SFC providers (E.g Net-Virt, GBP, SFC ) in OpenDaylight can listen to these OpenDaylight Neutron +Northbound SFC models and translate it to their specific yang models for classification/sfc. The +following diagram shows the high level integration between OpenStack and the OpenDaylight SFC +provider:: + + +---------------------------------------------+ + | OpenStack Network Server (networking-sfc) | + | +-------------------+ | + | | networking-odl | | + | | SFC Driver | | + | +-------------------+ | + +----------------------|----------------------+ + | REST Communication + | + ----------------------- + OpenDaylight Controller | | + +-----------------------|-----------------------|---------------+ + | +----------v----+ +---v---+ | + | Neutron | SFC Classifier| |SFC | Neutron | + | Northbound | Models | |Models | Northbound| + | Project +---------------+ +-------+ Project | + | / \ | | + | / \ | | + | / \ | | + | +-----V--+ +---V----+ +---V---+ | + | |Net-Virt| ... | GBP | | SFC | ... | + | +---------+ +--------+ +-------+ | + +-----------|----------------|------------------|---------------+ + | | | + | | | + +-----------V----------------V------------------V---------------+ + | Network/OVS | + | | + +---------------------------------------------------------------+ + +In the above architecture, the opendaylight components are shown just to understand the overall +architecture, but it's out of scope of this spec's work items. This spec will only track +progress related to networking-odl OpenStack sfc driver work. + +Given that OpenStack SFC APIs are port-pair based API's and OpenDaylight SFC API's are based on +IETF SFC yang models[8], there might be situations where translation might requires API enhancement +from OpenStack SFC. Networking SFC team is open for these new enhancement requirements given that +they are generic enough to be leveraged by other backend SFC providers[9]. This work will be +leveraging the POC work done by Tim [10] to come up with the first version of SFC driver. + +Dependencies +============ +It has a dependency on OpenDaylight Neutron Northbound SFC classifier and chain yang models, but +that is out of scope of this spec. + +Impact +====== +None + +Assignee(s) +=========== + +Following developers will be the initial contributor to the driver, but we will be happy to have +more contributor on board. + +* Anil Vishnoi (vishnoianil@gmail.com, irc: vishnoianil) +* Tim Rozet (trozet@redhat.com, irc: trozet) + +References +========== + +[1] http://docs.openstack.org/developer/networking-sfc/ + +[2] https://github.com/openstack/networking-sfc/blob/master/doc/source/api.rst + +[3] https://wiki.opendaylight.org/view/Service_Function_Chaining:Main + +[4] https://wiki.openstack.org/wiki/Tacker + +[5] https://github.com/trozet/tacker/tree/SFC_brahmaputra/tacker/sfc + +[6] https://github.com/trozet/tacker/tree/SFC_brahmaputra/tacker/sfc_classifier + +[7] https://tools.ietf.org/html/draft-ietf-netmod-acl-model-05 + +[8] https://wiki.opendaylight.org/view/NeutronNorthbound:Main + +[9] http://eavesdrop.openstack.org/meetings/service_chaining/2016/service_chaining.2016-03-31-17.00.log.html + +[10] https://github.com/trozet/tacker/blob/SFC_brahmaputra/tacker/sfc/drivers/opendaylight.py diff --git a/networking-odl/doc/source/specs.rst b/networking-odl/doc/source/specs.rst deleted file mode 100644 index 6050296..0000000 --- a/networking-odl/doc/source/specs.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - - neutron networking-odl specs documentation master file - -============================================= -Neutron networking-odl Project Specifications -============================================= - -Specs -===== - -.. toctree:: - :glob: - :maxdepth: 1 - - specs/* - -================== -Indices and tables -================== - -* :ref:`search` diff --git a/networking-odl/doc/source/specs/journal-recovery.rst b/networking-odl/doc/source/specs/journal-recovery.rst deleted file mode 100644 index 1132485..0000000 --- a/networking-odl/doc/source/specs/journal-recovery.rst +++ /dev/null @@ -1,152 +0,0 @@ -.. - This work is licensed under a Creative Commons Attribution 3.0 Unported - License. - - http://creativecommons.org/licenses/by/3.0/legalcode - -================ -Journal Recovery -================ - -https://blueprints.launchpad.net/networking-odl/+spec/journal-recovery - -Journal entries in the failed state need to be handled somehow. This spec will -try to address the issue and propose a solution. - -Problem Description -=================== - -Currently there is no handling for Journal entries that reach the failed state. -A journal entry can reach the failed state for several reasons, some of which -are: - -* Reached maximum failed attempts for retrying the operation. - -* Inconsistency between ODL and the Neutron DB. - - * For example: An update fails because the resource doesn't exist in ODL. - -* Bugs that can lead to failure to sync up. - -These entries will be left in the journal table forever which is a bit wasteful -since they take up some space on the DB storage and also affect the performance -of the journal table. -Albeit each entry has a negligble effect on it's own, the impact of a large -number of such entries can become quite significant. - -Proposed Change -=============== - -A "journal recovery" routine will run as part of the current journal -maintenance process. -This routine will scan the journal table for rows in the "failed" state and -will try to sync the resource for that entry. - -The procedure can be best described by the following flow chart: - -asciiflow:: - - +-----------------+ - | For each entry | - | in failed state | - +-------+---------+ - | - +-------v--------+ - | Query resource | - | on ODL (REST) | - +-----+-----+----+ - | | +-----------+ - Resource | | Determine | - exists +--Resource doesn't exist--> operation | - | | type | - +-----v-----+ +-----+-----+ - | Determine | | - | operation | | - | type | | - +-----+-----+ | - | +------------+ | - +--Create------> Mark entry <--Delete--+ - | | completed | | - | +----------^-+ Create/ - | | Update - | | | - | +------------+ | +-----v-----+ - +--Delete--> Mark entry | | | Determine | - | | pending | | | parent | - | +---------^--+ | | relation | - | | | +-----+-----+ - +-----v------+ | | | - | Compare to +--Different--+ | | - | resource | | | - | in DB +--Same------------+ | - +------------+ | - | - +-------------------+ | - | Create entry for <-----Has no parent------+ - | resource creation | | - +--------^----------+ Has a parent - | | - | +---------v-----+ - +------Parent exists------+ Query parent | - | on ODL (REST) | - +---------+-----+ - +------------------+ | - | Create entry for <---Parent doesn't exist--+ - | parent creation | - +------------------+ - -For every error during the process the entry will remain in failed state but -the error shouldn't stop processing of further entries. - - -The implementation could be done in two phases where the parent handling is -done in a a second pahse. -For the first phase if we detect an entry that is in failed for a create/update -operation and the resource doesn't exist on ODL we create a new "create -resource" journal entry for the resource. - -This propsal utilises the journal mechanism for it's operation while the only -part that deviates from the standard mode of operation is when it queries ODL -directly. This direct query has to be done to get ODL's representation of the -resource. - -Performance Impact ------------------- - -The maintenance thread will have another task to handle. This can lead to -longer processing time and even cause the thread to skip an iteration. -This is not an issue since the maintenance thread runs in parallel and doesn't -directly impact the responsiveness of the system. - -Since most operations here involve I/O then CPU probably won't be impacted. - -Network traffic would be impacted slightly since we will attempt to fetch the -resource each time from ODL and we might attempt to fetch it's parent. -This is however negligble as we do this only for failed entries, which are -expected to appear rarely. - - -Alternatives ------------- - -The partial sync process could make this process obsolete (along with full -sync), but it's a far more complicated and problematic process. -It's better to start with this process which is more lightweight and doable -and consider partial sync in the future. - - -Assignee(s) -=========== - -Primary assignee: - mkolesni <mkolesni@redhat.com> - -Other contributors: - None - - -References -========== - -https://goo.gl/IOMpzJ - diff --git a/networking-odl/doc/source/specs/qos-driver.rst b/networking-odl/doc/source/specs/qos-driver.rst deleted file mode 100644 index d2faad1..0000000 --- a/networking-odl/doc/source/specs/qos-driver.rst +++ /dev/null @@ -1,104 +0,0 @@ -========================================== -Quality of Service Driver for OpenDaylight -========================================== - -This spec describes the plan to implement quality of service driver for -OpenDaylight Controller. - -Problem Statement -================= -OpenStack networking project (neutron [1]) have a extension plugin implemented -and which expose api for quality of service that can be also be implemented by -any backend networking service provider to support QoS. These APIs provide a -way to integrate OpenStack Neutron QoS with any of the backend QoS providers. -OpenDaylight will provide backend for existing functionalities in neutron-QoS. -A notification driver is needed for integration of existing api in Openstack -neutron for QoS with OpenDaylight backend. - -Proposed Change -=============== -This change will introduce a new notification driver in networking-odl that -will take CRUD requests data for QoS policies from OpenStack neutron and notify -the OpenDaylight controller about the respective operation. - -Detailed Design -=============== -To enable the formal end to end integration between OpenStack QoS and -OpenDaylight requires an networking-odl QoS notification driver. QoS driver -will act as a shim layer between OpenStack and OpenDaylight that will carry -out following task: - -#. After getting QoS policy request data from neutron, It will log a operation - request in opendaylightjournal table. - -#. The operation will be picked from opendaylightjournal table and a rest call - for notifying OpenDaylight server will be prepared and sent. - -#. This request will processed by neutron northbound in OpenDaylight. -The OpenDaylight neutron northbound project. These models will be based -on the existing neutron qos plugin APIs. - -QoS providers in OpenDaylight can listen to these OpenDaylight Neutron -Northbound QoS models and translate it to their specific yang models for QoS. -The following diagram shows the high level integration between OpenStack and -the OpenDaylight QoS provider:: - - +---------------------------------------------+ - | OpenStack Network Server (neutron qos) | - | | - | +---------------------+ | - | | networking-odl | | - | | | | - | | +---------------| | - | | | Notification | | - | | | driver QoS | | - +----------------------|----------------------+ - | - | Rest Communication - | - OpenDaylight Controller | - +-----------------------|------------+ - | +----------V----+ | - | ODL | QoS Yang Model| | - | Northbound | | | - | (neutron) +---------------+ | - | | | - | | | - | ODL +----V----+ | - | Southbound | QoS | | - | (neutron) +---------+ | - +-----------------|------------------+ - | - | - +------------------------------------+ - | Network/OVS | - | | - +------------------------------------+ - -In the above diagram, the OpenDaylight components are shown just to understand -the overall architecture, but it's out of scope of this spec's work items. -This spec will only track progress related to networking-odl notification QoS -driver work. - -Dependencies -============ -It has a dependency on OpenDaylight Neutron Northbound QoS yang models, but -that is out of scope of this spec. - -Impact -====== -None - -Assignee(s) -=========== - -Following developers will be the initial contributor to the driver, but we -will be happy to have more contributor on board. - -* Manjeet Singh Bhatia (manjeet.s.bhatia@intel.com, irc: manjeets) - -References -========== - -[1] http://docs.openstack.org/developer/neutron/devref/quality_of_service.html -[2] https://wiki.opendaylight.org/view/NeutronNorthbound:Main diff --git a/networking-odl/doc/source/specs/sfc-driver.rst b/networking-odl/doc/source/specs/sfc-driver.rst deleted file mode 100644 index 388b2c1..0000000 --- a/networking-odl/doc/source/specs/sfc-driver.rst +++ /dev/null @@ -1,139 +0,0 @@ -================================================= -Service Function Chaining Driver for OpenDaylight -================================================= - -This spec describes the plan to implement OpenStack networking-sfc[1] driver -for OpenDaylight Controller. - -Problem Statement -=================== -OpenStack SFC project (networking-sfc [1]) exposes generic APIs[2] for Service -Function Chaining (SFC) that can be implemented by any backend networking -service provider to support SFC. These APIs provide a way to integrate -OpenStack SFC with any of the backend SFC providers. OpenDaylight SFC project -provides a very mature implementation of SFC [3], but currently there is no -formal integration mechanism present to consume OpenDaylight as an SFC provider -for networking-sfc. - -Recently Tacker project [4] has been approved as an official project in -OpenStack, that opens many possibilities to realize the NFV use cases (e.g SFC) -using OpenStack as a platform. Providing a formal end to end integration -between OpenStack and OpenDaylight for SFC use case will help NFV users -leverage OpenStack, Tacker and OpenDaylight as a solution. A POC for this -integration work has already been implemented [5][6] by Tim Rozet, but in -this POC work, Tacker directly communicates to OpenDaylight SFC & classifier -providers and not through OpenStack SFC APIs (networking-sfc). - -Proposed Change -=============== -Implementation of this spec will introduce a networking-sfc[1] driver for -OpenDaylight Controller in networking-odl project that will pass through -the networking-sfc API's call to the OpenDaylight Controller. - -Detailed Design -=============== -To enable the formal end to end integration between OpenStack SFC and -OpenDaylight requires an SFC driver for OpenDaylight. ODL SFC driver will -act as a shim layer between OpenStack and OpenDaylight that will carry out -following two main tasks: - -* Translation of OpenStack SFC Classifier API to ODL SFC classifier yang - models**. - -* Translation of OpenStack SFC API's to OpenDaylight Neutron Northbound - SFC models** [8]. - -** This work is not yet done, but the OpenDaylight neutron northbound project -needs to come up with yang models for SFC classification/chain. These models -will be based on the existing networking-sfc APIs. This work is out of scope -of networking-odl work and will be collaborated in the scope of OpenDaylight -Neutron Northbound project. - -SFC providers (E.g Net-Virt, GBP, SFC ) in OpenDaylight can listen to these -OpenDaylight Neutron Northbound SFC models and translate it to their specific -yang models for classification/sfc. The following diagram shows the high level -integration between OpenStack and the OpenDaylight SFC provider:: - - +---------------------------------------------+ - | OpenStack Network Server (networking-sfc) | - | +-------------------+ | - | | networking-odl | | - | | SFC Driver | | - | +-------------------+ | - +----------------------|----------------------+ - | REST Communication - | - ----------------------- - OpenDaylight Controller | | - +-----------------------|-----------------------|---------------+ - | +----------v----+ +---v---+ | - | Neutron | SFC Classifier| |SFC | Neutron | - | Northbound | Models | |Models | Northbound| - | Project +---------------+ +-------+ Project | - | / \ | | - | / \ | | - | / \ | | - | +-----V--+ +---V----+ +---V---+ | - | |Net-Virt| ... | GBP | | SFC | ... | - | +---------+ +--------+ +-------+ | - +-----------|----------------|------------------|---------------+ - | | | - | | | - +-----------V----------------V------------------V---------------+ - | Network/OVS | - | | - +---------------------------------------------------------------+ - -In the above architecture, the opendaylight components are shown just to -understand the overall architecture, but it's out of scope of this spec's -work items. This spec will only track progress related to networking-odl -OpenStack sfc driver work. - -Given that OpenStack SFC APIs are port-pair based API's and OpenDaylight SFC -API's are based on IETF SFC yang models[8], there might be situations where -translation might requires API enhancement from OpenStack SFC. Networking SFC -team is open for these new enhancement requirements given that they are generic -enough to be leveraged by other backend SFC providers[9]. This work will be -leveraging the POC work done by Tim [10] to come up with the first version of -SFC driver. - -Dependencies -============ -It has a dependency on OpenDaylight Neutron Northbound SFC classifier and chain -yang models, but that is out of scope of this spec. - -Impact -====== -None - -Assignee(s) -=========== - -Following developers will be the initial contributor to the driver, but we will -be happy to have more contributor on board. - -* Anil Vishnoi (vishnoianil@gmail.com, irc: vishnoianil) -* Tim Rozet (trozet@redhat.com, irc: trozet) - -References -========== - -[1] http://docs.openstack.org/developer/networking-sfc/ - -[2] https://github.com/openstack/networking-sfc/blob/master/doc/source/api.rst - -[3] https://wiki.opendaylight.org/view/Service_Function_Chaining:Main - -[4] https://wiki.openstack.org/wiki/Tacker - -[5] https://github.com/trozet/tacker/tree/SFC_brahmaputra/tacker/sfc - -[6] https://github.com/trozet/tacker/tree/SFC_brahmaputra/tacker/sfc_classifier - -[7] https://tools.ietf.org/html/draft-ietf-netmod-acl-model-05 - -[8] https://wiki.opendaylight.org/view/NeutronNorthbound:Main - -[9] http://eavesdrop.openstack.org/meetings/service_chaining/2016/service_chaining.2016-03-31-17.00.log.html - -[10] https://github.com/trozet/tacker/blob/SFC_brahmaputra/tacker/sfc/drivers/opendaylight.py diff --git a/networking-odl/etc/neutron/plugins/ml2/ml2_conf_odl.ini b/networking-odl/etc/neutron/plugins/ml2/ml2_conf_odl.ini index 8218073..b70ac8b 100644 --- a/networking-odl/etc/neutron/plugins/ml2/ml2_conf_odl.ini +++ b/networking-odl/etc/neutron/plugins/ml2/ml2_conf_odl.ini @@ -40,22 +40,3 @@ # # retry_count = 5 # Example: retry_count = 5 - -# (IntOpt) (V2 driver) Journal maintenance operations interval in seconds. -# -# maintenance_interval = 300 -# Example: maintenance_interval = 30 - -# (IntOpt) (V2 driver) Time to keep completed rows in seconds. -# Completed rows retention will be checked every maintenance_interval by the -# cleanup thread. -# To disable completed rows deletion value should be -1 -# -# completed_rows_retention = 600 -# Example: completed_rows_retention = 30 - -# (IntOpt) (V2 driver) Timeout in seconds to wait before marking a processing -# row back to pending state. -# -# processing_timeout = 100 -# Example: maintenance_interval = 200 diff --git a/networking-odl/networking_odl/cmd/__init__.py b/networking-odl/networking_odl/cmd/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/networking-odl/networking_odl/cmd/__init__.py +++ /dev/null diff --git a/networking-odl/networking_odl/cmd/set_ovs_hostconfigs.py b/networking-odl/networking_odl/cmd/set_ovs_hostconfigs.py deleted file mode 100644 index 8b8b1d3..0000000 --- a/networking-odl/networking_odl/cmd/set_ovs_hostconfigs.py +++ /dev/null @@ -1,123 +0,0 @@ -# Copyright (c) 2016 OpenStack Foundation -# 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. - -from oslo_config import cfg -from oslo_log import log -from oslo_serialization import jsonutils - -from neutron._i18n import _ -from neutron._i18n import _LE -from neutron._i18n import _LI -from neutron.agent.common import utils -from neutron.common import config - -LOG = log.getLogger(__name__) - - -class SetOvsHostconfigs(object): - - # Refer below for ovs ext-id strings - # https://review.openstack.org/#/c/309630/ - extid_str = 'external_ids:{}={}' - odl_os_hconf_str = 'odl_os_hostconfig_config_{}' - odl_os_hostid_str = 'odl_os_hostconfig_hostid' - odl_os_hosttype_str = 'odl_os_hostconfig_hosttype' - - # TODO(mzmalick): use neutron.agent.ovsdb instead of subprocess.Popen - ovs_cmd_get_uuid = ['ovs-vsctl', 'get', 'Open_vSwitch', '.', '_uuid'] - ovs_cmd_set_extid = ['ovs-vsctl', 'set', 'Open_vSwitch', '', ''] - - UUID = 3 - EXTID = 4 - - def __init__(self): - self.ovs_uuid = self.get_ovs_uuid() - - def ovs_exec_cmd(self, cmd): - LOG.info(_LI("SET-HOSTCONFIGS: Executing cmd: %s"), ' '.join(cmd)) - return utils.execute(cmd, return_stderr=True, run_as_root=True) - - def get_ovs_uuid(self): - return self.ovs_exec_cmd(self.ovs_cmd_get_uuid)[0].strip() - - def set_extid_hostname(self, hname): - self.ovs_cmd_set_extid[self.UUID] = self.ovs_uuid - self.ovs_cmd_set_extid[self.EXTID] = self.extid_str.format( - self.odl_os_hostid_str, hname) - return self.ovs_exec_cmd(self.ovs_cmd_set_extid) - - def set_extid_hosttype(self, htype): - self.ovs_cmd_set_extid[self.UUID] = self.ovs_uuid - self.ovs_cmd_set_extid[self.EXTID] = self.extid_str.format( - self.odl_os_hosttype_str, htype) - return self.ovs_exec_cmd(self.ovs_cmd_set_extid) - - def set_extid_hostconfig(self, htype, hconfig): - ext_htype = self.odl_os_hconf_str.format( - htype.lower().replace(' ', '_')) - self.ovs_cmd_set_extid[self.UUID] = self.ovs_uuid - self.ovs_cmd_set_extid[self.EXTID] = self.extid_str.format( - ext_htype, jsonutils.dumps(hconfig)) - return self.ovs_exec_cmd(self.ovs_cmd_set_extid) - - def set_ovs_extid_hostconfigs(self, conf): - if not conf.ovs_hostconfigs: - LOG.error(_LE("ovs_hostconfigs argument needed!")) - return - - json_str = cfg.CONF.ovs_hostconfigs - json_str.replace("\'", "\"") - LOG.debug("SET-HOSTCONFIGS: JSON String %s", json_str) - - self.set_extid_hostname(cfg.CONF.host) - htype_config = jsonutils.loads(json_str) - - for htype in htype_config.keys(): - self.set_extid_hostconfig(htype, htype_config[htype]) - - -def setup_conf(): - """setup cmdline options.""" - cli_opts = [ - cfg.StrOpt('ovs_hostconfigs', help=_( - "OVS hostconfiguration for OpenDaylight " - "as a JSON string")) - ] - - conf = cfg.CONF - conf.register_cli_opts(cli_opts) - conf.import_opt('host', 'neutron.common.config') - conf() - return conf - - -def main(): - - conf = setup_conf() - config.setup_logging() - SetOvsHostconfigs().set_ovs_extid_hostconfigs(conf) - -# -# command line example (run without line breaks): -# -# set_ovs_hostconfigs.py --ovs_hostconfigs='{"ODL L2": { -# "supported_vnic_types":[{"vnic_type":"normal", "vif_type":"ovs", -# "vif_details":{}}], "allowed_network_types":["local","vlan", -# "vxlan","gre"], "bridge_mappings":{"physnet1":"br-ex"}}, -# "ODL L3": {}}' --debug -# - -if __name__ == '__main__': - main() diff --git a/networking-odl/networking_odl/cmd/test_setup_hostconfig.sh b/networking-odl/networking_odl/cmd/test_setup_hostconfig.sh deleted file mode 100755 index 1651d0e..0000000 --- a/networking-odl/networking_odl/cmd/test_setup_hostconfig.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/bin/sh - -python set_ovs_hostconfigs.py --debug --ovs_hostconfigs='{"ODL L2": {"supported_vnic_types":[{"vnic_type":"normal", "vif_type":"ovs", "vif_details":{}}], "allowed_network_types":["local","vlan", "vxlan","gre"], "bridge_mappings":{"physnet1":"br-ex"}}, "ODL L3": {"some_details": "dummy_details"}}' diff --git a/networking-odl/networking_odl/common/cache.py b/networking-odl/networking_odl/common/cache.py index 6c44cc3..8b5287e 100644 --- a/networking-odl/networking_odl/common/cache.py +++ b/networking-odl/networking_odl/common/cache.py @@ -45,9 +45,6 @@ class CacheEntry(collections.namedtuple('CacheEntry', ['timeout', 'values'])): def __eq__(self, other): return self is other - def __ne__(self, other): - return not self.__eq__(other) - class Cache(object): '''Generic mapping class used to cache mapping diff --git a/networking-odl/networking_odl/common/callback.py b/networking-odl/networking_odl/common/callback.py index d9d168b..fe09037 100644 --- a/networking-odl/networking_odl/common/callback.py +++ b/networking-odl/networking_odl/common/callback.py @@ -13,8 +13,6 @@ # License for the specific language governing permissions and limitations # under the License. -import collections - from oslo_log import log as logging from neutron.callbacks import events @@ -25,49 +23,79 @@ from networking_odl.common import constants as odl_const LOG = logging.getLogger(__name__) -ODLResource = collections.namedtuple('ODLResource', ('singular', 'plural')) -_RESOURCE_MAPPING = { - resources.SECURITY_GROUP: ODLResource(odl_const.ODL_SG, odl_const.ODL_SGS), - resources.SECURITY_GROUP_RULE: ODLResource(odl_const.ODL_SG_RULE, - odl_const.ODL_SG_RULES), -} -_OPERATION_MAPPING = { - events.AFTER_CREATE: odl_const.ODL_CREATE, - events.AFTER_UPDATE: odl_const.ODL_UPDATE, - events.AFTER_DELETE: odl_const.ODL_DELETE, -} - class OdlSecurityGroupsHandler(object): - def __init__(self, odl_driver): - self.odl_driver = odl_driver - self._subscribe() + def __init__(self, odl_client, event_type="AFTER"): + self.odl_client = odl_client + self.subscribe(event_type) - def _subscribe(self): - for event in (events.AFTER_CREATE, events.AFTER_DELETE): - registry.subscribe(self.sg_callback, resources.SECURITY_GROUP, - event) - registry.subscribe(self.sg_callback, resources.SECURITY_GROUP_RULE, - event) + def sg_callback(self, resource, event, trigger, **kwargs): - registry.subscribe(self.sg_callback, resources.SECURITY_GROUP, - events.AFTER_UPDATE) + res_key_mapping = { + odl_const.ODL_SGS: odl_const.ODL_SG, + odl_const.ODL_SG_RULES: odl_const.ODL_SG_RULE, + } + res_name_mapping = { + resources.SECURITY_GROUP: odl_const.ODL_SGS, + resources.SECURITY_GROUP_RULE: odl_const.ODL_SG_RULES, + } + ops_mapping = { + events.AFTER_CREATE: odl_const.ODL_CREATE, + events.AFTER_UPDATE: odl_const.ODL_UPDATE, + events.AFTER_DELETE: odl_const.ODL_DELETE, + events.PRECOMMIT_CREATE: odl_const.ODL_CREATE, + events.PRECOMMIT_UPDATE: odl_const.ODL_UPDATE, + events.PRECOMMIT_DELETE: odl_const.ODL_DELETE, + } - def sg_callback(self, resource, event, trigger, **kwargs): + # Loop up the ODL's counterpart resource label + # e.g. resources.SECURITY_GROUP -> odl_const.ODL_SGS + # Note: 1) url will use dashes instead of underscore; + # 2) when res is a list, append 's' to odl_res_key + # Ref: https://github.com/opendaylight/neutron/blob/master + # /northbound-api/src/main/java/org/opendaylight + # /neutron/northbound/api + # /NeutronSecurityGroupRequest.java#L33 res = kwargs.get(resource) res_id = kwargs.get("%s_id" % resource) - odl_res_type = _RESOURCE_MAPPING[resource] + odl_res_type = res_name_mapping[resource] + odl_res_key = res_key_mapping[odl_res_type] + odl_ops = ops_mapping[event] + odl_res_type_uri = odl_res_type.replace('_', '-') + + if type(res) is list: + odl_res_key += "s" - odl_ops = _OPERATION_MAPPING[event] - odl_res_dict = None if res is None else {odl_res_type.singular: res} + if res is None: + odl_res_dict = None + else: + odl_res_dict = {odl_res_key: res} LOG.debug("Calling sync_from_callback with ODL_OPS (%(odl_ops)s) " "ODL_RES_TYPE (%(odl_res_type)s) RES_ID (%(res_id)s) " - "ODL_RES_DICT (%(odl_res_dict)s) KWARGS (%(kwargs)s)", + "ODL_RES_KEY (%(odl_res_key)s) RES (%(res)s) " + "KWARGS (%(kwargs)s)", {'odl_ops': odl_ops, 'odl_res_type': odl_res_type, - 'res_id': res_id, 'odl_res_dict': odl_res_dict, + 'res_id': res_id, 'odl_res_key': odl_res_key, 'res': res, 'kwargs': kwargs}) - self.odl_driver.sync_from_callback(odl_ops, odl_res_type, - res_id, odl_res_dict) + self.odl_client.sync_from_callback(odl_ops, odl_res_type_uri, res_id, + odl_res_dict) + + def subscribe(self, event_type): + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP, + getattr(events, "%s_CREATE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP, + getattr(events, "%s_UPDATE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP, + getattr(events, "%s_DELETE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP_RULE, + getattr(events, "%s_CREATE" % event_type)) + registry.subscribe( + self.sg_callback, resources.SECURITY_GROUP_RULE, + getattr(events, "%s_DELETE" % event_type)) diff --git a/networking-odl/networking_odl/common/client.py b/networking-odl/networking_odl/common/client.py index 45349e9..537665d 100644 --- a/networking-odl/networking_odl/common/client.py +++ b/networking-odl/networking_odl/common/client.py @@ -27,9 +27,9 @@ cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') class OpenDaylightRestClient(object): @classmethod - def create_client(cls, url=None): + def create_client(cls): if cfg.CONF.ml2_odl.enable_lightweight_testing: - LOG.debug("ODL lightweight testing is enabled, " + LOG.debug("ODL lightweight testing is enabled, ", "returning a OpenDaylightLwtClient instance") """Have to import at here, otherwise we create a dependency loop""" @@ -37,7 +37,7 @@ class OpenDaylightRestClient(object): cls = lwt.OpenDaylightLwtClient return cls( - url or cfg.CONF.ml2_odl.url, + cfg.CONF.ml2_odl.url, cfg.CONF.ml2_odl.username, cfg.CONF.ml2_odl.password, cfg.CONF.ml2_odl.timeout) diff --git a/networking-odl/networking_odl/common/config.py b/networking-odl/networking_odl/common/config.py index c921242..0e38e2f 100644 --- a/networking-odl/networking_odl/common/config.py +++ b/networking-odl/networking_odl/common/config.py @@ -34,34 +34,12 @@ odl_opts = [ cfg.IntOpt('retry_count', default=5, help=_("(V2 driver) Number of times to retry a row " "before failing.")), - cfg.IntOpt('maintenance_interval', default=300, - help=_("(V2 driver) Journal maintenance operations interval " - "in seconds.")), - cfg.IntOpt('completed_rows_retention', default=600, - help=_("(V2 driver) Time to keep completed rows in seconds." - "Completed rows retention will be checked every " - "maintenance_interval by the cleanup thread." - "To disable completed rows deletion " - "value should be -1")), cfg.BoolOpt('enable_lightweight_testing', default=False, help=_('Test without real ODL.')), cfg.StrOpt('port_binding_controller', default='network-topology', - help=_('Name of the controller to be used for port binding.')), - cfg.IntOpt('processing_timeout', default='100', - help=_("(V2 driver) Time in seconds to wait before a " - "processing row is marked back to pending.")), - cfg.StrOpt('odl_hostconf_uri', - help=_("Path for ODL host configuration REST interface"), - default="/restconf/operational/neutron:neutron/hostconfigs"), - cfg.IntOpt('restconf_poll_interval', default=30, - help=_("Poll interval in seconds for getting ODL hostconfig")), - + help=_('Name of the controller to be used for port binding.')) ] cfg.CONF.register_opts(odl_opts, "ml2_odl") - - -def list_opts(): - return [('ml2_odl', odl_opts)] diff --git a/networking-odl/networking_odl/common/constants.py b/networking-odl/networking_odl/common/constants.py index 50c0117..9fed790 100644 --- a/networking-odl/networking_odl/common/constants.py +++ b/networking-odl/networking_odl/common/constants.py @@ -24,10 +24,8 @@ ODL_SGS = 'security_groups' ODL_SG_RULE = 'security_group_rule' ODL_SG_RULES = 'security_group_rules' ODL_ROUTER = 'router' -ODL_ROUTERS = 'routers' ODL_ROUTER_INTF = 'router_interface' ODL_FLOATINGIP = 'floatingip' -ODL_FLOATINGIPS = 'floatingips' ODL_LOADBALANCER = 'loadbalancer' ODL_LOADBALANCERS = 'loadbalancers' diff --git a/networking-odl/networking_odl/common/exceptions.py b/networking-odl/networking_odl/common/exceptions.py index f174c10..59956b1 100644 --- a/networking-odl/networking_odl/common/exceptions.py +++ b/networking-odl/networking_odl/common/exceptions.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. -from neutron_lib import exceptions as exc +from neutron.common import exceptions as exc class OpendaylightAuthError(exc.NeutronException): diff --git a/networking-odl/networking_odl/common/filters.py b/networking-odl/networking_odl/common/filters.py index fb42a0e..340fa4d 100644 --- a/networking-odl/networking_odl/common/filters.py +++ b/networking-odl/networking_odl/common/filters.py @@ -12,85 +12,158 @@ # 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 abc +import six + from networking_odl.common import constants as odl_const from networking_odl.common import utils as odl_utils -def _filter_unmapped_null(resource_dict, unmapped_keys): - # NOTE(yamahata): bug work around - # https://bugs.eclipse.org/bugs/show_bug.cgi?id=475475 - # Null-value for an unmapped element causes next mapped - # collection to contain a null value - # JSON: { "unmappedField": null, "mappedCollection": [ "a" ] } - # - # Java Object: - # class Root { - # Collection<String> mappedCollection = new ArrayList<String>; - # } - # - # Result: - # Field B contains one element; null - # - # TODO(yamahata): update along side with neutron and ODL - # add when neutron adds more extensions - # delete when ODL neutron northbound supports it - # TODO(yamahata): do same thing for other resources - keys_to_del = [key for key in unmapped_keys - if resource_dict.get(key) is None] - if keys_to_del: - odl_utils.try_del(resource_dict, keys_to_del) - - -_NETWORK_UNMAPPED_KEYS = ['qos_policy_id'] -_PORT_UNMAPPED_KEYS = ['binding:profile', 'dns_name', - 'port_security_enabled', 'qos_policy_id'] - - -def _filter_network_create(network): - odl_utils.try_del(network, ['status', 'subnets']) - _filter_unmapped_null(network, _NETWORK_UNMAPPED_KEYS) - - -def _filter_network_update(network): - odl_utils.try_del(network, ['id', 'status', 'subnets', 'tenant_id']) - _filter_unmapped_null(network, _NETWORK_UNMAPPED_KEYS) - - -def _filter_subnet_update(subnet): - odl_utils.try_del(subnet, ['id', 'network_id', 'ip_version', 'cidr', - 'allocation_pools', 'tenant_id']) - - -def _filter_port_create(port): - """Filter out port attributes not required for a create.""" - odl_utils.try_del(port, ['status']) - _filter_unmapped_null(port, _PORT_UNMAPPED_KEYS) - - -def _filter_port_update(port): - """Filter out port attributes for an update operation.""" - odl_utils.try_del(port, ['network_id', 'id', 'status', 'mac_address', - 'tenant_id', 'fixed_ips']) - _filter_unmapped_null(port, _PORT_UNMAPPED_KEYS) - - -def _filter_router_update(router): - """Filter out attributes for an update operation.""" - odl_utils.try_del(router, ['id', 'tenant_id', 'status']) - - -_FILTER_MAP = { - (odl_const.ODL_NETWORK, odl_const.ODL_CREATE): _filter_network_create, - (odl_const.ODL_NETWORK, odl_const.ODL_UPDATE): _filter_network_update, - (odl_const.ODL_SUBNET, odl_const.ODL_UPDATE): _filter_subnet_update, - (odl_const.ODL_PORT, odl_const.ODL_CREATE): _filter_port_create, - (odl_const.ODL_PORT, odl_const.ODL_UPDATE): _filter_port_update, - (odl_const.ODL_ROUTER, odl_const.ODL_UPDATE): _filter_router_update, +@six.add_metaclass(abc.ABCMeta) +class ResourceFilterBase(object): + @staticmethod + @abc.abstractmethod + def filter_create_attributes(resource): + pass + + @staticmethod + @abc.abstractmethod + def filter_update_attributes(resource): + pass + + +class NetworkFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(network): + """Filter out network attributes not required for a create.""" + odl_utils.try_del(network, ['status', 'subnets']) + + @staticmethod + def filter_update_attributes(network): + """Filter out network attributes for an update operation.""" + odl_utils.try_del(network, ['id', 'status', 'subnets', 'tenant_id']) + + +class SubnetFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(subnet): + """Filter out subnet attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(subnet): + """Filter out subnet attributes for an update operation.""" + odl_utils.try_del(subnet, ['id', 'network_id', 'ip_version', 'cidr', + 'allocation_pools', 'tenant_id']) + + +class PortFilter(ResourceFilterBase): + @staticmethod + def _filter_unmapped_null(port): + # NOTE(yamahata): bug work around + # https://bugs.eclipse.org/bugs/show_bug.cgi?id=475475 + # Null-value for an unmapped element causes next mapped + # collection to contain a null value + # JSON: { "unmappedField": null, "mappedCollection": [ "a" ] } + # + # Java Object: + # class Root { + # Collection<String> mappedCollection = new ArrayList<String>; + # } + # + # Result: + # Field B contains one element; null + # + # TODO(yamahata): update along side with neutron and ODL + # add when neutron adds more extensions + # delete when ODL neutron northbound supports it + # TODO(yamahata): do same thing for other resources + unmapped_keys = ['dns_name', 'port_security_enabled', + 'binding:profile'] + keys_to_del = [key for key in unmapped_keys if port.get(key) is None] + if keys_to_del: + odl_utils.try_del(port, keys_to_del) + + @classmethod + def filter_create_attributes(cls, port): + """Filter out port attributes not required for a create.""" + cls._filter_unmapped_null(port) + odl_utils.try_del(port, ['status']) + + @classmethod + def filter_update_attributes(cls, port): + """Filter out port attributes for an update operation.""" + cls._filter_unmapped_null(port) + odl_utils.try_del(port, ['network_id', 'id', 'status', 'mac_address', + 'tenant_id', 'fixed_ips']) + + +class SecurityGroupFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(sg): + """Filter out security-group attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(sg): + """Filter out security-group attributes for an update operation.""" + pass + + +class SecurityGroupRuleFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(sg_rule): + """Filter out sg-rule attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(sg_rule): + """Filter out sg-rule attributes for an update operation.""" + pass + + +class RouterFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(router): + """Filter out attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(router): + """Filter out attributes for an update operation.""" + odl_utils.try_del(router, ['id', 'tenant_id', 'status']) + + +class FloatingIPFilter(ResourceFilterBase): + @staticmethod + def filter_create_attributes(floatingip): + """Filter out attributes not required for a create.""" + pass + + @staticmethod + def filter_update_attributes(floatingip): + """Filter out attributes for an update operation.""" + pass + + +class RouterIntfFilter(ResourceFilterBase): + @staticmethod + def filter_add_attributes(routerintf): + """Filter out attributes not required for a create.""" + pass + + @staticmethod + def filter_remove_attributes(routerintf): + """Filter out attributes for an update operation.""" + pass + +FILTER_MAP = { + odl_const.ODL_NETWORK: NetworkFilter, + odl_const.ODL_SUBNET: SubnetFilter, + odl_const.ODL_PORT: PortFilter, + odl_const.ODL_ROUTER: RouterFilter, + odl_const.ODL_ROUTER_INTF: RouterIntfFilter, + odl_const.ODL_FLOATINGIP: FloatingIPFilter, + odl_const.ODL_SG: SecurityGroupFilter, + odl_const.ODL_SG_RULE: SecurityGroupRuleFilter, } - - -def filter_for_odl(object_type, operation, data): - """Filter out the attributed before sending the data to ODL""" - filter_key = (object_type, operation) - if filter_key in _FILTER_MAP: - _FILTER_MAP[filter_key](data) diff --git a/networking-odl/networking_odl/common/lightweight_testing.py b/networking-odl/networking_odl/common/lightweight_testing.py index 3d0cf2e..3f9c2bc 100644 --- a/networking-odl/networking_odl/common/lightweight_testing.py +++ b/networking-odl/networking_odl/common/lightweight_testing.py @@ -20,7 +20,6 @@ import six from oslo_log import log as logging from oslo_serialization import jsonutils -from networking_odl._i18n import _ from networking_odl.common import client from networking_odl.common import constants as odl_const @@ -69,7 +68,7 @@ class OpenDaylightLwtClient(client.OpenDaylightRestClient): """No ID in URL, elements in resource_list must have ID""" if resource_list is None: - raise ValueError(_("resource_list can not be None")) + raise ValueError("resource_list can not be None") for resource in resource_list: if resource['id'] in resource_dict: @@ -88,7 +87,7 @@ class OpenDaylightLwtClient(client.OpenDaylightRestClient): resource_id = cls._get_resource_id(urlpath) if resource_list is None: - raise ValueError(_("resource_list can not be None")) + raise ValueError("resource_list can not be None") if resource_id and len(resource_list) != 1: LOG.debug("Updating %s with multiple resources", urlpath) diff --git a/networking-odl/networking_odl/db/db.py b/networking-odl/networking_odl/db/db.py index 31f4ce2..a8e7ade 100644 --- a/networking-odl/networking_odl/db/db.py +++ b/networking-odl/networking_odl/db/db.py @@ -12,8 +12,6 @@ # 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 datetime - from sqlalchemy import asc from sqlalchemy import func from sqlalchemy import or_ @@ -163,72 +161,3 @@ def create_pending_row(session, object_type, object_uuid, # Keep session flush for unit tests. NOOP for L2/L3 events since calls are # made inside database session transaction with subtransactions=True. session.flush() - - -@db_api.retry_db_errors -def delete_pending_rows(session, operations_to_delete): - with session.begin(): - session.query(models.OpendaylightJournal).filter( - models.OpendaylightJournal.operation.in_(operations_to_delete), - models.OpendaylightJournal.state == odl_const.PENDING).delete( - synchronize_session=False) - session.expire_all() - - -@db_api.retry_db_errors -def _update_maintenance_state(session, expected_state, state): - with session.begin(): - row = session.query(models.OpendaylightMaintenance).filter_by( - state=expected_state).with_for_update().one_or_none() - if row is None: - return False - - row.state = state - return True - - -def lock_maintenance(session): - return _update_maintenance_state(session, odl_const.PENDING, - odl_const.PROCESSING) - - -def unlock_maintenance(session): - return _update_maintenance_state(session, odl_const.PROCESSING, - odl_const.PENDING) - - -def update_maintenance_operation(session, operation=None): - """Update the current maintenance operation details. - - The function assumes the lock is held, so it mustn't be run outside of a - locked context. - """ - op_text = None - if operation: - op_text = operation.__name__ - - with session.begin(): - row = session.query(models.OpendaylightMaintenance).one_or_none() - row.processing_operation = op_text - - -def delete_rows_by_state_and_time(session, state, time_delta): - with session.begin(): - now = session.execute(func.now()).scalar() - session.query(models.OpendaylightJournal).filter( - models.OpendaylightJournal.state == state, - models.OpendaylightJournal.last_retried < now - time_delta).delete( - synchronize_session=False) - session.expire_all() - - -def reset_processing_rows(session, max_timedelta): - with session.begin(): - now = session.execute(func.now()).scalar() - max_timedelta = datetime.timedelta(seconds=max_timedelta) - rows = session.query(models.OpendaylightJournal).filter( - models.OpendaylightJournal.last_retried < now - max_timedelta, - models.OpendaylightJournal.state == odl_const.PROCESSING, - ).update({'state': odl_const.PENDING}) - - return rows diff --git a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/EXPAND_HEAD b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/EXPAND_HEAD index 34912ba..95ad199 100644 --- a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -703dbf02afde +37e242787ae5 diff --git a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/contract/383acb0d38a0_initial_contract.py b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/contract/383acb0d38a0_initial_contract.py index 43959c0..5a81be5 100644 --- a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/contract/383acb0d38a0_initial_contract.py +++ b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/contract/383acb0d38a0_initial_contract.py @@ -19,7 +19,6 @@ Create Date: 2015-09-03 22:27:49.306394 """ -from neutron.db import migration from neutron.db.migration import cli @@ -28,9 +27,6 @@ revision = '383acb0d38a0' down_revision = 'b89a299e19f9' branch_labels = (cli.CONTRACT_BRANCH,) -# milestone identifier, used by neutron-db-manage -neutron_milestone = [migration.MITAKA] - def upgrade(): pass diff --git a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/expand/37e242787ae5_opendaylight_neutron_mechanism_driver_.py b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/expand/37e242787ae5_opendaylight_neutron_mechanism_driver_.py index 71d8273..b78993d 100644 --- a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/expand/37e242787ae5_opendaylight_neutron_mechanism_driver_.py +++ b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/mitaka/expand/37e242787ae5_opendaylight_neutron_mechanism_driver_.py @@ -20,17 +20,10 @@ Revises: 247501328046 Create Date: 2015-10-30 22:09:27.221767 """ -from neutron.db import migration - - # revision identifiers, used by Alembic. revision = '37e242787ae5' down_revision = '247501328046' -# milestone identifier, used by neutron-db-manage -neutron_milestone = [migration.MITAKA] - - from alembic import op import sqlalchemy as sa @@ -44,8 +37,7 @@ def upgrade(): sa.Column('operation', sa.String(36), nullable=False), sa.Column('data', sa.PickleType, nullable=True), sa.Column('state', - sa.Enum('pending', 'processing', 'failed', 'completed', - name='state'), + sa.Enum('pending', 'processing', 'failed', 'completed'), nullable=False, default='pending'), sa.Column('retry_count', sa.Integer, default=0), sa.Column('created_at', sa.DateTime, default=sa.func.now()), diff --git a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/newton/expand/703dbf02afde_add_journal_maintenance_table.py b/networking-odl/networking_odl/db/migration/alembic_migrations/versions/newton/expand/703dbf02afde_add_journal_maintenance_table.py deleted file mode 100644 index bbe0c46..0000000 --- a/networking-odl/networking_odl/db/migration/alembic_migrations/versions/newton/expand/703dbf02afde_add_journal_maintenance_table.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright 2016 Red Hat Inc. -# -# 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. -# - -"""Add journal maintenance table - -Revision ID: 703dbf02afde -Revises: 37e242787ae5 -Create Date: 2016-04-12 10:49:31.802663 - -""" - -# revision identifiers, used by Alembic. -revision = '703dbf02afde' -down_revision = '37e242787ae5' - -from alembic import op -from oslo_utils import uuidutils -import sqlalchemy as sa - -from networking_odl.common import constants as odl_const - - -def upgrade(): - maint_table = op.create_table( - 'opendaylight_maintenance', - sa.Column('id', sa.String(36), primary_key=True), - sa.Column('state', sa.Enum(odl_const.PENDING, odl_const.PROCESSING, - name='state'), - nullable=False), - sa.Column('processing_operation', sa.String(70)), - sa.Column('lock_updated', sa.TIMESTAMP, nullable=False, - server_default=sa.func.now(), - onupdate=sa.func.now()) - ) - - # Insert the only row here that is used to synchronize the lock between - # different Neutron processes. - op.bulk_insert(maint_table, - [{'id': uuidutils.generate_uuid(), - 'state': odl_const.PENDING}]) diff --git a/networking-odl/networking_odl/db/models.py b/networking-odl/networking_odl/db/models.py index 0416ed1..94c3ef0 100644 --- a/networking-odl/networking_odl/db/models.py +++ b/networking-odl/networking_odl/db/models.py @@ -34,14 +34,3 @@ class OpendaylightJournal(model_base.BASEV2, HasId): created_at = sa.Column(sa.DateTime, server_default=sa.func.now()) last_retried = sa.Column(sa.TIMESTAMP, server_default=sa.func.now(), onupdate=sa.func.now()) - - -class OpendaylightMaintenance(model_base.BASEV2, HasId): - __tablename__ = 'opendaylight_maintenance' - - state = sa.Column(sa.Enum(odl_const.PENDING, odl_const.PROCESSING), - nullable=False) - processing_operation = sa.Column(sa.String(70)) - lock_updated = sa.Column(sa.TIMESTAMP, nullable=False, - server_default=sa.func.now(), - onupdate=sa.func.now()) diff --git a/networking-odl/networking_odl/journal/cleanup.py b/networking-odl/networking_odl/journal/cleanup.py deleted file mode 100644 index 994fb82..0000000 --- a/networking-odl/networking_odl/journal/cleanup.py +++ /dev/null @@ -1,46 +0,0 @@ -# -# Copyright (C) 2016 Red Hat, Inc. -# -# 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 datetime import timedelta - -from oslo_config import cfg -from oslo_log import log as logging - -from networking_odl._i18n import _LI -from networking_odl.common import constants as odl_const -from networking_odl.db import db - -LOG = logging.getLogger(__name__) - - -class JournalCleanup(object): - """Journal maintenance operation for deleting completed rows.""" - def __init__(self): - self._rows_retention = cfg.CONF.ml2_odl.completed_rows_retention - self._processing_timeout = cfg.CONF.ml2_odl.processing_timeout - - def delete_completed_rows(self, session): - if self._rows_retention is not -1: - LOG.debug("Deleting completed rows") - db.delete_rows_by_state_and_time( - session, odl_const.COMPLETED, - timedelta(seconds=self._rows_retention)) - - def cleanup_processing_rows(self, session): - row_count = db.reset_processing_rows(session, self._processing_timeout) - if row_count: - LOG.info(_LI("Reset %(num)s orphaned rows back to pending"), - {"num": row_count}) diff --git a/networking-odl/networking_odl/journal/dependency_validations.py b/networking-odl/networking_odl/journal/dependency_validations.py index a6f5f96..07c657c 100644 --- a/networking-odl/networking_odl/journal/dependency_validations.py +++ b/networking-odl/networking_odl/journal/dependency_validations.py @@ -235,7 +235,7 @@ def validate_security_group_rule_operation(session, row): """ return True -_VALIDATION_MAP = { +VALIDATION_MAP = { odl_const.ODL_NETWORK: validate_network_operation, odl_const.ODL_SUBNET: validate_subnet_operation, odl_const.ODL_PORT: validate_port_operation, @@ -245,23 +245,3 @@ _VALIDATION_MAP = { odl_const.ODL_SG: validate_security_group_operation, odl_const.ODL_SG_RULE: validate_security_group_rule_operation, } - - -def validate(session, row): - """Validate resource dependency in journaled operations. - - :param session: db session - :param row: entry in journal entry to be validated - """ - return _VALIDATION_MAP[row.object_type](session, row) - - -def register_validator(object_type, validator): - """Register validator function for given resource. - - :param object_type: neutron resource type - :param validator: function to be registered which validates resource - dependencies - """ - assert object_type not in _VALIDATION_MAP - _VALIDATION_MAP[object_type] = validator diff --git a/networking-odl/networking_odl/journal/full_sync.py b/networking-odl/networking_odl/journal/full_sync.py deleted file mode 100644 index dad7215..0000000 --- a/networking-odl/networking_odl/journal/full_sync.py +++ /dev/null @@ -1,114 +0,0 @@ -# -# Copyright (C) 2016 Red Hat, Inc. -# -# 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 requests - -from neutron import context as neutron_context -from neutron import manager -from neutron.plugins.common import constants -from neutron_lib import constants as l3_constants - -from networking_odl.common import client -from networking_odl.common import constants as odl_const -from networking_odl.db import db - -# Define which pending operation types should be deleted -_CANARY_NETWORK_ID = "bd8db3a8-2b30-4083-a8b3-b3fd46401142" -_CANARY_TENANT_ID = "bd8db3a8-2b30-4083-a8b3-b3fd46401142" -_CANARY_NETWORK_DATA = {'id': _CANARY_NETWORK_ID, - 'tenant_id': _CANARY_TENANT_ID, - 'name': 'Sync Canary Network', - 'admin_state_up': False} -_OPS_TO_DELETE_ON_SYNC = (odl_const.ODL_CREATE, odl_const.ODL_UPDATE) -_L2_RESOURCES_TO_SYNC = [(odl_const.ODL_SG, odl_const.ODL_SGS), - (odl_const.ODL_SG_RULE, odl_const.ODL_SG_RULES), - (odl_const.ODL_NETWORK, odl_const.ODL_NETWORKS), - (odl_const.ODL_SUBNET, odl_const.ODL_SUBNETS), - (odl_const.ODL_PORT, odl_const.ODL_PORTS)] -_L3_RESOURCES_TO_SYNC = [(odl_const.ODL_ROUTER, odl_const.ODL_ROUTERS), - (odl_const.ODL_FLOATINGIP, odl_const.ODL_FLOATINGIPS)] -_CLIENT = client.OpenDaylightRestClient.create_client() - - -def full_sync(session): - if not _full_sync_needed(session): - return - - db.delete_pending_rows(session, _OPS_TO_DELETE_ON_SYNC) - - dbcontext = neutron_context.get_admin_context() - plugin = manager.NeutronManager.get_plugin() - for resource_type, collection_name in _L2_RESOURCES_TO_SYNC: - _sync_resources(session, plugin, dbcontext, resource_type, - collection_name) - - l3plugin = manager.NeutronManager.get_service_plugins().get( - constants.L3_ROUTER_NAT) - for resource_type, collection_name in _L3_RESOURCES_TO_SYNC: - _sync_resources(session, l3plugin, dbcontext, resource_type, - collection_name) - _sync_router_ports(session, plugin, dbcontext) - - db.create_pending_row(session, odl_const.ODL_NETWORK, _CANARY_NETWORK_ID, - odl_const.ODL_CREATE, _CANARY_NETWORK_DATA) - - -def _full_sync_needed(session): - return (_canary_network_missing_on_odl() and - _canary_network_not_in_journal(session)) - - -def _canary_network_missing_on_odl(): - # Try to reach the ODL server, sometimes it might be up & responding to - # HTTP calls but inoperative.. - response = _CLIENT.get(odl_const.ODL_NETWORKS) - response.raise_for_status() - - response = _CLIENT.get(odl_const.ODL_NETWORKS + "/" + _CANARY_NETWORK_ID) - if response.status_code == requests.codes.not_found: - return True - - # In case there was an error raise it up because we don't know how to deal - # with it.. - response.raise_for_status() - return False - - -def _canary_network_not_in_journal(session): - return not db.check_for_pending_or_processing_ops(session, - _CANARY_NETWORK_ID, - odl_const.ODL_CREATE) - - -def _sync_resources(session, plugin, dbcontext, object_type, collection_name): - obj_getter = getattr(plugin, 'get_%s' % collection_name) - resources = obj_getter(dbcontext) - - for resource in resources: - db.create_pending_row(session, object_type, resource['id'], - odl_const.ODL_CREATE, resource) - - -def _sync_router_ports(session, plugin, dbcontext): - filters = {'device_owner': [l3_constants.DEVICE_OWNER_ROUTER_INTF]} - router_ports = plugin.get_ports(dbcontext, filters=filters) - for port in router_ports: - resource = {'subnet_id': port['fixed_ips'][0]['subnet_id'], - 'port_id': port['id'], - 'id': port['device_id'], - 'tenant_id': port['tenant_id']} - db.create_pending_row(session, odl_const.ODL_ROUTER_INTF, port['id'], - odl_const.ODL_ADD, resource) diff --git a/networking-odl/networking_odl/journal/journal.py b/networking-odl/networking_odl/journal/journal.py index ca0d2c2..26295b3 100644 --- a/networking-odl/networking_odl/journal/journal.py +++ b/networking-odl/networking_odl/journal/journal.py @@ -21,9 +21,7 @@ from requests import exceptions from oslo_config import cfg from oslo_log import log as logging -from neutron import context as neutron_context from neutron.db import api as neutron_db_api -from neutron import manager from networking_odl.common import client from networking_odl.common import constants as odl_const @@ -44,51 +42,6 @@ def call_thread_on_end(func): return new_func -def _enrich_port(db_session, context, object_type, operation, data): - """Enrich the port with additional information needed by ODL""" - if context: - plugin = context._plugin - dbcontext = context._plugin_context - else: - dbcontext = neutron_context.get_admin_context() - plugin = manager.NeutronManager.get_plugin() - - groups = [plugin.get_security_group(dbcontext, sg) - for sg in data['security_groups']] - new_data = copy.deepcopy(data) - new_data['security_groups'] = groups - - # NOTE(yamahata): work around for port creation for router - # tenant_id=''(empty string) is passed when port is created - # by l3 plugin internally for router. - # On the other hand, ODL doesn't accept empty string for tenant_id. - # In that case, deduce tenant_id from network_id for now. - # Right fix: modify Neutron so that don't allow empty string - # for tenant_id even for port for internal use. - # TODO(yamahata): eliminate this work around when neutron side - # is fixed - # assert port['tenant_id'] != '' - if ('tenant_id' not in new_data or new_data['tenant_id'] == ''): - if context: - tenant_id = context._network_context._network['tenant_id'] - else: - network = plugin.get_network(dbcontext, new_data['network_id']) - tenant_id = network['tenant_id'] - new_data['tenant_id'] = tenant_id - - return new_data - - -def record(db_session, object_type, object_uuid, operation, data, - context=None): - if (object_type == odl_const.ODL_PORT and - operation in (odl_const.ODL_CREATE, odl_const.ODL_UPDATE)): - data = _enrich_port(db_session, context, object_type, operation, data) - - db.create_pending_row(db_session, object_type, object_uuid, operation, - data) - - class OpendaylightJournalThread(object): """Thread worker for the Opendaylight Journal Database.""" def __init__(self): @@ -123,28 +76,40 @@ class OpendaylightJournalThread(object): self._timer.start() def _json_data(self, row): - data = copy.deepcopy(row.data) - filters.filter_for_odl(row.object_type, row.operation, data) + filter_cls = filters.FILTER_MAP[row.object_type] url_object = row.object_type.replace('_', '-') if row.operation == odl_const.ODL_CREATE: method = 'post' + attr_filter = filter_cls.filter_create_attributes + data = copy.deepcopy(row.data) urlpath = url_object + 's' + attr_filter(data) to_send = {row.object_type: data} elif row.operation == odl_const.ODL_UPDATE: method = 'put' + attr_filter = filter_cls.filter_update_attributes + data = copy.deepcopy(row.data) urlpath = url_object + 's/' + row.object_uuid + attr_filter(data) to_send = {row.object_type: data} elif row.operation == odl_const.ODL_DELETE: method = 'delete' + data = None urlpath = url_object + 's/' + row.object_uuid to_send = None elif row.operation == odl_const.ODL_ADD: method = 'put' + attr_filter = filter_cls.filter_add_attributes + data = copy.deepcopy(row.data) + attr_filter(data) urlpath = 'routers/' + data['id'] + '/add_router_interface' to_send = data elif row.operation == odl_const.ODL_REMOVE: method = 'put' + attr_filter = filter_cls.filter_remove_attributes + data = copy.deepcopy(row.data) + attr_filter(data) urlpath = 'routers/' + data['id'] + '/remove_router_interface' to_send = data @@ -177,7 +142,9 @@ class OpendaylightJournalThread(object): break # Validate the operation - valid = dependency_validations.validate(session, row) + validate_func = (dependency_validations. + VALIDATION_MAP[row.object_type]) + valid = validate_func(session, row) if not valid: LOG.info(_LI("%(operation)s %(type)s %(uuid)s is not a " "valid operation yet, skipping for now"), diff --git a/networking-odl/networking_odl/journal/maintenance.py b/networking-odl/networking_odl/journal/maintenance.py deleted file mode 100644 index 7fb82a0..0000000 --- a/networking-odl/networking_odl/journal/maintenance.py +++ /dev/null @@ -1,73 +0,0 @@ -# -# Copyright (C) 2016 Red Hat, Inc. -# -# 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.db import api as neutron_db_api -from oslo_config import cfg -from oslo_log import log as logging -from oslo_service import loopingcall - -from networking_odl._i18n import _LI, _LE -from networking_odl.db import db - - -LOG = logging.getLogger(__name__) - - -class MaintenanceThread(object): - def __init__(self): - self.timer = loopingcall.FixedIntervalLoopingCall(self.execute_ops) - self.maintenance_interval = cfg.CONF.ml2_odl.maintenance_interval - self.maintenance_ops = [] - - def start(self): - self.timer.start(self.maintenance_interval, stop_on_exception=False) - - def _execute_op(self, operation, session): - op_details = operation.__name__ - if operation.__doc__: - op_details += " (%s)" % operation.func_doc - - try: - LOG.info(_LI("Starting maintenance operation %s."), op_details) - db.update_maintenance_operation(session, operation=operation) - operation(session=session) - LOG.info(_LI("Finished maintenance operation %s."), op_details) - except Exception: - LOG.exception(_LE("Failed during maintenance operation %s."), - op_details) - - def execute_ops(self): - LOG.info(_LI("Starting journal maintenance run.")) - session = neutron_db_api.get_session() - if not db.lock_maintenance(session): - LOG.info(_LI("Maintenance already running, aborting.")) - return - - try: - for operation in self.maintenance_ops: - self._execute_op(operation, session) - finally: - db.update_maintenance_operation(session, operation=None) - db.unlock_maintenance(session) - LOG.info(_LI("Finished journal maintenance run.")) - - def register_operation(self, f): - """Register a function to be run by the maintenance thread. - - :param f: Function to call when the thread runs. The function will - receive a DB session to use for DB operations. - """ - self.maintenance_ops.append(f) diff --git a/networking-odl/networking_odl/l3/l3_odl.py b/networking-odl/networking_odl/l3/l3_odl.py index e06e335..36d8779 100644 --- a/networking-odl/networking_odl/l3/l3_odl.py +++ b/networking-odl/networking_odl/l3/l3_odl.py @@ -19,6 +19,7 @@ from oslo_log import log as logging from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api from neutron.api.rpc.handlers import l3_rpc +from neutron.common import constants as q_const from neutron.common import rpc as n_rpc from neutron.common import topics from neutron.db import extraroute_db @@ -26,7 +27,6 @@ from neutron.db import l3_agentschedulers_db from neutron.db import l3_dvr_db from neutron.db import l3_gwmode_db from neutron.plugins.common import constants -from neutron_lib import constants as q_const from networking_odl.common import client as odl_client from networking_odl.common import utils as odl_utils diff --git a/networking-odl/networking_odl/l3/l3_odl_v2.py b/networking-odl/networking_odl/l3/l3_odl_v2.py index 2732ea6..da5829b 100644..100755 --- a/networking-odl/networking_odl/l3/l3_odl_v2.py +++ b/networking-odl/networking_odl/l3/l3_odl_v2.py @@ -16,6 +16,7 @@ from oslo_log import log as logging +from neutron.common import constants as q_const from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron.db import extraroute_db @@ -23,7 +24,6 @@ from neutron.db import l3_agentschedulers_db from neutron.db import l3_dvr_db from neutron.db import l3_gwmode_db from neutron.plugins.common import constants -from neutron_lib import constants as q_const from networking_odl.common import config # noqa from networking_odl.common import constants as odl_const diff --git a/networking-odl/networking_odl/lbaas/driver_v1.py b/networking-odl/networking_odl/lbaas/driver_v1.py index aaf3dcf..0d66f70 100644 --- a/networking-odl/networking_odl/lbaas/driver_v1.py +++ b/networking-odl/networking_odl/lbaas/driver_v1.py @@ -20,13 +20,10 @@ from oslo_log import log as logging from neutron_lbaas.services.loadbalancer.drivers import abstract_driver from networking_odl.common import client as odl_client -from networking_odl.common import constants as odl_const + cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') LOG = logging.getLogger(__name__) -LBAAS = "lbaas" -POOLS_URL_PATH = LBAAS + '/' + odl_const.ODL_POOLS -HEALTHMONITORS_URL_PATH = LBAAS + '/' + odl_const.ODL_HEALTHMONITORS class OpenDaylightLbaasDriverV1(abstract_driver.LoadBalancerAbstractDriver): @@ -43,82 +40,53 @@ class OpenDaylightLbaasDriverV1(abstract_driver.LoadBalancerAbstractDriver): self.client = odl_client.OpenDaylightRestClient.create_client() def create_vip(self, context, vip): - """Create a vip on the OpenDaylight Controller. - - No code related to vip in the OpenDayLight neutronNorthbound, - so pass this method. - """ + """Create a vip on the OpenDaylight Controller.""" pass def update_vip(self, context, old_vip, vip): - """Update a vip on the OpenDaylight Controller. - - No code related to vip in the OpenDayLight neutronNorthbound, - so pass this method. - """ + """Update a vip on the OpenDaylight Controller.""" pass def delete_vip(self, context, vip): - """Delete a vip on the OpenDaylight Controller. - - No code related to vip in the OpenDayLight neutronNorthbound, - so pass this method. - """ + """Delete a vip on the OpenDaylight Controller.""" pass def create_pool(self, context, pool): """Create a pool on the OpenDaylight Controller.""" - url = POOLS_URL_PATH - self.client.sendjson('post', url, {odl_const.ODL_POOL: pool}) + pass def update_pool(self, context, old_pool, pool): """Update a pool on the OpenDaylight Controller.""" - url = POOLS_URL_PATH + "/" + old_pool['id'] - self.client.sendjson('put', url, {odl_const.ODL_POOL: pool}) + pass def delete_pool(self, context, pool): """Delete a pool on the OpenDaylight Controller.""" - url = POOLS_URL_PATH + "/" + pool['id'] - self.client.sendjson('delete', url, None) + pass def create_member(self, context, member): """Create a pool member on the OpenDaylight Controller.""" - url = ( - POOLS_URL_PATH + '/' + member['pool_id'] + - '/' + odl_const.ODL_MEMBERS) - self.client.sendjson('post', url, {odl_const.ODL_MEMBER: member}) + pass def update_member(self, context, old_member, member): """Update a pool member on the OpenDaylight Controller.""" - url = ( - POOLS_URL_PATH + '/' + member['pool_id'] + - '/' + odl_const.ODL_MEMBERS + "/" + old_member['id']) - self.client.sendjson('put', url, {odl_const.ODL_MEMBER: member}) + pass def delete_member(self, context, member): """Delete a pool member on the OpenDaylight Controller.""" - url = ( - POOLS_URL_PATH + '/' + member['pool_id'] + - '/' + odl_const.ODL_MEMBERS + "/" + member['id']) - self.client.sendjson('delete', url, None) + pass def create_pool_health_monitor(self, context, health_monitor, pool_id): """Create a pool health monitor on the OpenDaylight Controller.""" - url = HEALTHMONITORS_URL_PATH - self.client.sendjson( - 'post', url, {odl_const.ODL_HEALTHMONITOR: health_monitor}) + pass def update_pool_health_monitor(self, context, old_health_monitor, health_monitor, pool_id): """Update a pool health monitor on the OpenDaylight Controller.""" - url = HEALTHMONITORS_URL_PATH + "/" + old_health_monitor['id'] - self.client.sendjson( - 'put', url, {odl_const.ODL_HEALTHMONITOR: health_monitor}) + pass def delete_pool_health_monitor(self, context, health_monitor, pool_id): """Delete a pool health monitor on the OpenDaylight Controller.""" - url = HEALTHMONITORS_URL_PATH + "/" + health_monitor['id'] - self.client.sendjson('delete', url, None) + pass def stats(self, context, pool_id): """Retrieve pool statistics from the OpenDaylight Controller.""" diff --git a/networking-odl/networking_odl/ml2/legacy_port_binding.py b/networking-odl/networking_odl/ml2/legacy_port_binding.py index 7b9b918..18cf95f 100644 --- a/networking-odl/networking_odl/ml2/legacy_port_binding.py +++ b/networking-odl/networking_odl/ml2/legacy_port_binding.py @@ -16,10 +16,10 @@ from oslo_log import log +from neutron.common import constants as n_const from neutron.extensions import portbindings from neutron.plugins.common import constants from neutron.plugins.ml2 import driver_api -from neutron_lib import constants as n_const from networking_odl.ml2 import port_binding @@ -31,18 +31,11 @@ class LegacyPortBindingManager(port_binding.PortBindingController): def __init__(self): self.vif_details = {portbindings.CAP_PORT_FILTER: True} - self.supported_vnic_types = [portbindings.VNIC_NORMAL] def bind_port(self, port_context): """Set binding for all valid segments """ - vnic_type = port_context.current.get(portbindings.VNIC_TYPE, - portbindings.VNIC_NORMAL) - if vnic_type not in self.supported_vnic_types: - LOG.debug("Refusing to bind due to unsupported vnic_type: %s", - vnic_type) - return valid_segment = None for segment in port_context.segments_to_bind: diff --git a/networking-odl/networking_odl/ml2/mech_driver.py b/networking-odl/networking_odl/ml2/mech_driver.py index adde8d9..2d60e7a 100644 --- a/networking-odl/networking_odl/ml2/mech_driver.py +++ b/networking-odl/networking_odl/ml2/mech_driver.py @@ -23,13 +23,13 @@ from oslo_log import log as logging from oslo_utils import excutils import requests +from neutron.common import exceptions as n_exc from neutron.common import utils from neutron import context as neutron_context from neutron.extensions import allowedaddresspairs as addr_pair from neutron.extensions import securitygroup as sg from neutron.plugins.ml2 import driver_api from neutron.plugins.ml2 import driver_context -from neutron_lib import exceptions as n_exc from networking_odl._i18n import _LE from networking_odl.common import callback as odl_call @@ -67,46 +67,17 @@ class ResourceFilterBase(object): def filter_create_attributes_with_plugin(resource, plugin, dbcontext): pass - @staticmethod - def _filter_unmapped_null(resource_dict, unmapped_keys): - # NOTE(yamahata): bug work around - # https://bugs.eclipse.org/bugs/show_bug.cgi?id=475475 - # Null-value for an unmapped element causes next mapped - # collection to contain a null value - # JSON: { "unmappedField": null, "mappedCollection": [ "a" ] } - # - # Java Object: - # class Root { - # Collection<String> mappedCollection = new ArrayList<String>; - # } - # - # Result: - # Field B contains one element; null - # - # TODO(yamahata): update along side with neutron and ODL - # add when neutron adds more extensions - # delete when ODL neutron northbound supports it - # TODO(yamahata): do same thing for other resources - keys_to_del = [key for key in unmapped_keys - if resource_dict.get(key) is None] - if keys_to_del: - odl_utils.try_del(resource_dict, keys_to_del) - class NetworkFilter(ResourceFilterBase): - _UNMAPPED_KEYS = ['qos_policy_id'] - - @classmethod - def filter_create_attributes(cls, network, context): + @staticmethod + def filter_create_attributes(network, context): """Filter out network attributes not required for a create.""" odl_utils.try_del(network, ['status', 'subnets']) - cls._filter_unmapped_null(network, cls._UNMAPPED_KEYS) - @classmethod - def filter_update_attributes(cls, network, context): + @staticmethod + def filter_update_attributes(network, context): """Filter out network attributes for an update operation.""" odl_utils.try_del(network, ['id', 'status', 'subnets', 'tenant_id']) - cls._filter_unmapped_null(network, cls._UNMAPPED_KEYS) @classmethod def filter_create_attributes_with_plugin(cls, network, plugin, dbcontext): @@ -135,9 +106,6 @@ class SubnetFilter(ResourceFilterBase): class PortFilter(ResourceFilterBase): - _UNMAPPED_KEYS = ['binding:profile', 'dns_name', - 'port_security_enabled', 'qos_policy_id'] - @staticmethod def _add_security_groups(port, context): """Populate the 'security_groups' field with entire records.""" @@ -154,12 +122,38 @@ class PortFilter(ResourceFilterBase): network_address = str(netaddr.IPNetwork(ip_address)) address_pair['ip_address'] = network_address + @staticmethod + def _filter_unmapped_null(port): + # NOTE(yamahata): bug work around + # https://bugs.eclipse.org/bugs/show_bug.cgi?id=475475 + # Null-value for an unmapped element causes next mapped + # collection to contain a null value + # JSON: { "unmappedField": null, "mappedCollection": [ "a" ] } + # + # Java Object: + # class Root { + # Collection<String> mappedCollection = new ArrayList<String>; + # } + # + # Result: + # Field B contains one element; null + # + # TODO(yamahata): update along side with neutron and ODL + # add when neutron adds more extensions + # delete when ODL neutron northbound supports it + # TODO(yamahata): do same thing for other resources + unmapped_keys = ['dns_name', 'port_security_enabled', + 'binding:profile'] + keys_to_del = [key for key in unmapped_keys if port.get(key) is None] + if keys_to_del: + odl_utils.try_del(port, keys_to_del) + @classmethod def filter_create_attributes(cls, port, context): """Filter out port attributes not required for a create.""" cls._add_security_groups(port, context) cls._fixup_allowed_ipaddress_pairs(port[addr_pair.ADDRESS_PAIRS]) - cls._filter_unmapped_null(port, cls._UNMAPPED_KEYS) + cls._filter_unmapped_null(port) odl_utils.try_del(port, ['status']) # NOTE(yamahata): work around for port creation for router @@ -181,7 +175,7 @@ class PortFilter(ResourceFilterBase): """Filter out port attributes for an update operation.""" cls._add_security_groups(port, context) cls._fixup_allowed_ipaddress_pairs(port[addr_pair.ADDRESS_PAIRS]) - cls._filter_unmapped_null(port, cls._UNMAPPED_KEYS) + cls._filter_unmapped_null(port) odl_utils.try_del(port, ['network_id', 'id', 'status', 'tenant_id']) @classmethod @@ -363,8 +357,8 @@ class OpenDaylightDriver(object): 'object_id': obj_id}) self.out_of_sync = True - def sync_from_callback(self, operation, res_type, res_id, resource_dict): - object_type = res_type.plural.replace('_', '-') + def sync_from_callback(self, operation, object_type, res_id, + resource_dict): try: if operation == odl_const.ODL_DELETE: self.out_of_sync |= not self.client.try_delete( @@ -380,8 +374,7 @@ class OpenDaylightDriver(object): except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE("Unable to perform %(operation)s on " - "%(object_type)s %(res_id)s " - "%(resource_dict)s"), + "%(object_type)s %(res_id)s %(resource_dict)s"), {'operation': operation, 'object_type': object_type, 'res_id': res_id, @@ -419,40 +412,31 @@ class OpenDaylightMechanismDriver(driver_api.MechanismDriver): # Postcommit hooks are used to trigger synchronization. def create_network_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_CREATE, odl_const.ODL_NETWORKS, - context) + self.odl_drv.synchronize('create', odl_const.ODL_NETWORKS, context) def update_network_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_UPDATE, odl_const.ODL_NETWORKS, - context) + self.odl_drv.synchronize('update', odl_const.ODL_NETWORKS, context) def delete_network_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_DELETE, odl_const.ODL_NETWORKS, - context) + self.odl_drv.synchronize('delete', odl_const.ODL_NETWORKS, context) def create_subnet_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_CREATE, odl_const.ODL_SUBNETS, - context) + self.odl_drv.synchronize('create', odl_const.ODL_SUBNETS, context) def update_subnet_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_UPDATE, odl_const.ODL_SUBNETS, - context) + self.odl_drv.synchronize('update', odl_const.ODL_SUBNETS, context) def delete_subnet_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_DELETE, odl_const.ODL_SUBNETS, - context) + self.odl_drv.synchronize('delete', odl_const.ODL_SUBNETS, context) def create_port_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_CREATE, odl_const.ODL_PORTS, - context) + self.odl_drv.synchronize('create', odl_const.ODL_PORTS, context) def update_port_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_UPDATE, odl_const.ODL_PORTS, - context) + self.odl_drv.synchronize('update', odl_const.ODL_PORTS, context) def delete_port_postcommit(self, context): - self.odl_drv.synchronize(odl_const.ODL_DELETE, odl_const.ODL_PORTS, - context) + self.odl_drv.synchronize('delete', odl_const.ODL_PORTS, context) def bind_port(self, context): self.odl_drv.bind_port(context) diff --git a/networking-odl/networking_odl/ml2/mech_driver_v2.py b/networking-odl/networking_odl/ml2/mech_driver_v2.py index dfc8df1..6fc199b 100644 --- a/networking-odl/networking_odl/ml2/mech_driver_v2.py +++ b/networking-odl/networking_odl/ml2/mech_driver_v2.py @@ -12,6 +12,7 @@ # 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 copy from oslo_config import cfg from oslo_log import log as logging @@ -21,11 +22,8 @@ from neutron.plugins.ml2 import driver_api as api from networking_odl.common import callback from networking_odl.common import config as odl_conf -from networking_odl.common import constants as odl_const -from networking_odl.journal import cleanup -from networking_odl.journal import full_sync +from networking_odl.db import db from networking_odl.journal import journal -from networking_odl.journal import maintenance from networking_odl.ml2 import port_binding LOG = logging.getLogger(__name__) @@ -44,66 +42,83 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): self.sg_handler = callback.OdlSecurityGroupsHandler(self) self.journal = journal.OpendaylightJournalThread() self.port_binding_controller = port_binding.PortBindingManager.create() - self._start_maintenance_thread() - - def _start_maintenance_thread(self): - # start the maintenance thread and register all the maintenance - # operations : - # (1) JournalCleanup - Delete completed rows from journal - # (2) CleanupProcessing - Mark orphaned processing rows to pending - # (3) Full sync - Re-sync when detecting an ODL "cold reboot" - cleanup_obj = cleanup.JournalCleanup() - self._maintenance_thread = maintenance.MaintenanceThread() - self._maintenance_thread.register_operation( - cleanup_obj.delete_completed_rows) - self._maintenance_thread.register_operation( - cleanup_obj.cleanup_processing_rows) - self._maintenance_thread.register_operation(full_sync.full_sync) - self._maintenance_thread.start() - - @staticmethod - def _record_in_journal(context, object_type, operation, data=None): - if data is None: - data = context.current - journal.record(context._plugin_context.session, object_type, - context.current['id'], operation, data) def create_network_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_NETWORK, odl_const.ODL_CREATE) + db.create_pending_row(context._plugin_context.session, 'network', + context.current['id'], 'create', context.current) def create_subnet_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_SUBNET, odl_const.ODL_CREATE) + db.create_pending_row(context._plugin_context.session, 'subnet', + context.current['id'], 'create', context.current) def create_port_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_PORT, odl_const.ODL_CREATE) + dbcontext = context._plugin_context + groups = [context._plugin.get_security_group(dbcontext, sg) + for sg in context.current['security_groups']] + new_context = copy.deepcopy(context.current) + new_context['security_groups'] = groups + # NOTE(yamahata): work around for port creation for router + # tenant_id=''(empty string) is passed when port is created + # by l3 plugin internally for router. + # On the other hand, ODL doesn't accept empty string for tenant_id. + # In that case, deduce tenant_id from network_id for now. + # Right fix: modify Neutron so that don't allow empty string + # for tenant_id even for port for internal use. + # TODO(yamahata): eliminate this work around when neutron side + # is fixed + # assert port['tenant_id'] != '' + if ('tenant_id' not in context.current or + context.current['tenant_id'] == ''): + tenant_id = context._network_context._network['tenant_id'] + new_context['tenant_id'] = tenant_id + db.create_pending_row(context._plugin_context.session, 'port', + context.current['id'], 'create', new_context) def update_network_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_NETWORK, odl_const.ODL_UPDATE) + db.create_pending_row(context._plugin_context.session, 'network', + context.current['id'], 'update', context.current) def update_subnet_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_SUBNET, odl_const.ODL_UPDATE) + db.create_pending_row(context._plugin_context.session, 'subnet', + context.current['id'], 'update', context.current) def update_port_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_PORT, odl_const.ODL_UPDATE) + port = context._plugin.get_port(context._plugin_context, + context.current['id']) + dbcontext = context._plugin_context + new_context = copy.deepcopy(context.current) + groups = [context._plugin.get_security_group(dbcontext, sg) + for sg in port['security_groups']] + new_context['security_groups'] = groups + # Add the network_id in for validation + new_context['network_id'] = port['network_id'] + # NOTE(yamahata): work around for port creation for router + # tenant_id=''(empty string) is passed when port is created + # by l3 plugin internally for router. + # On the other hand, ODL doesn't accept empty string for tenant_id. + # In that case, deduce tenant_id from network_id for now. + # Right fix: modify Neutron so that don't allow empty string + # for tenant_id even for port for internal use. + # TODO(yamahata): eliminate this work around when neutron side + # is fixed + # assert port['tenant_id'] != '' + if ('tenant_id' not in context.current or + context.current['tenant_id'] == ''): + port['tenant_id'] = context._network_context._network['tenant_id'] + db.create_pending_row(context._plugin_context.session, 'port', + context.current['id'], 'update', new_context) def delete_network_precommit(self, context): - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_NETWORK, odl_const.ODL_DELETE, data=[]) + db.create_pending_row(context._plugin_context.session, 'network', + context.current['id'], 'delete', None) def delete_subnet_precommit(self, context): # Use the journal row's data field to store parent object # uuids. This information is required for validation checking # when deleting parent objects. new_context = [context.current['network_id']] - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_SUBNET, odl_const.ODL_DELETE, - data=new_context) + db.create_pending_row(context._plugin_context.session, 'subnet', + context.current['id'], 'delete', new_context) def delete_port_precommit(self, context): # Use the journal row's data field to store parent object @@ -112,19 +127,19 @@ class OpenDaylightMechanismDriver(api.MechanismDriver): new_context = [context.current['network_id']] for subnet in context.current['fixed_ips']: new_context.append(subnet['subnet_id']) - OpenDaylightMechanismDriver._record_in_journal( - context, odl_const.ODL_PORT, odl_const.ODL_DELETE, - data=new_context) + db.create_pending_row(context._plugin_context.session, 'port', + context.current['id'], 'delete', new_context) @journal.call_thread_on_end - def sync_from_callback(self, operation, res_type, res_id, resource_dict): - object_type = res_type.singular + def sync_from_callback(self, operation, res_type_uri, res_id, + resource_dict): + object_type = res_type_uri.replace('-', '_')[:-1] object_uuid = (resource_dict[object_type]['id'] if operation == 'create' else res_id) if resource_dict is not None: resource_dict = resource_dict[object_type] - journal.record(db_api.get_session(), object_type, object_uuid, - operation, resource_dict) + db.create_pending_row(db_api.get_session(), object_type, object_uuid, + operation, resource_dict) def _postcommit(self, context): self.journal.set_sync_event() diff --git a/networking-odl/networking_odl/ml2/network_topology.py b/networking-odl/networking_odl/ml2/network_topology.py index b0bfae1..99137a8 100644 --- a/networking-odl/networking_odl/ml2/network_topology.py +++ b/networking-odl/networking_odl/ml2/network_topology.py @@ -27,7 +27,7 @@ from oslo_serialization import jsonutils from networking_odl.common import cache from networking_odl.common import client from networking_odl.common import utils -from networking_odl._i18n import _, _LI, _LW, _LE +from networking_odl._i18n import _LI, _LW, _LE from networking_odl.ml2 import port_binding @@ -44,7 +44,8 @@ class NetworkTopologyManager(port_binding.PortBindingController): # List of class names of registered implementations of interface # NetworkTopologyParser network_topology_parsers = [ - 'networking_odl.ml2.ovsdb_topology.OvsdbNetworkTopologyParser'] + 'networking_odl.ml2.ovsdb_topology.OvsdbNetworkTopologyParser', + 'networking_odl.ml2.vpp_topology.VppNetworkTopologyParser'] def __init__(self, vif_details=None, client=None): # Details for binding port @@ -65,6 +66,7 @@ class NetworkTopologyManager(port_binding.PortBindingController): """ host_name = port_context.host + LOG.debug('Processing port for host: %s', host_name) elements = list() try: # Append to empty list to add as much elements as possible @@ -85,6 +87,7 @@ class NetworkTopologyManager(port_binding.PortBindingController): {'host_name': host_name}) # Imported here to avoid cyclic module dependencies + # TODO(wdec): Add vpp topology import from networking_odl.ml2 import ovsdb_topology elements = [ovsdb_topology.OvsdbNetworkTopologyElement()] @@ -100,7 +103,8 @@ class NetworkTopologyManager(port_binding.PortBindingController): # it is invalid for at least one element: discard it vif_type_is_valid_for_all = False break - + # TODO(wdec): This needs to deal with not all network elements + # supporting all binding types. if vif_type_is_valid_for_all: # This is the best VIF type valid for all elements LOG.debug( @@ -206,13 +210,14 @@ class NetworkTopologyManager(port_binding.PortBindingController): try: for element in parser.parse_network_topology(network_topology): if not isinstance(element, NetworkTopologyElement): - raise TypeError(_( + raise TypeError( "Yield element doesn't implement interface " - "'NetworkTopologyElement': {!r}").format(element)) + "'NetworkTopologyElement': {!r}".format(element)) # the same element can be known by more host addresses for host_address in element.host_addresses: if host_address in addresses: at_least_one_element_for_asked_addresses = True + LOG.debug("Found cached Host: %s \n", host_address) yield host_address, element except Exception: LOG.exception( @@ -224,8 +229,8 @@ class NetworkTopologyManager(port_binding.PortBindingController): # calling this method again as soon it is requested and avoid # waiting for cache expiration raise ValueError( - _('No such topology element for given host addresses: {}') - .format(', '.join(addresses))) + 'No such topology element for given host addresses: {}'.format( + ', '.join(addresses))) @six.add_metaclass(abc.ABCMeta) @@ -240,9 +245,9 @@ class NetworkTopologyParser(object): module = importlib.import_module(module_name) clss = getattr(module, class_name) if not issubclass(clss, cls): - raise TypeError(_( + raise TypeError( "Class {class_name!r} of module {module_name!r} doesn't " - "implement 'NetworkTopologyParser' interface.").format( + "implement 'NetworkTopologyParser' interface.".format( class_name=class_name, module_name=module_name)) return clss() diff --git a/networking-odl/networking_odl/ml2/ovsdb_topology.py b/networking-odl/networking_odl/ml2/ovsdb_topology.py index f2c8ad8..ed82032 100644 --- a/networking-odl/networking_odl/ml2/ovsdb_topology.py +++ b/networking-odl/networking_odl/ml2/ovsdb_topology.py @@ -21,12 +21,11 @@ from oslo_log import log import six from six.moves.urllib import parse +from neutron.common import constants as n_const from neutron.extensions import portbindings from neutron.plugins.common import constants from neutron.plugins.ml2 import driver_api -from neutron_lib import constants as n_const -from networking_odl._i18n import _ from networking_odl.ml2 import network_topology @@ -171,8 +170,7 @@ class OvsdbNetworkTopologyElement(network_topology.NetworkTopologyElement): status=n_const.PORT_STATUS_ACTIVE) return - raise ValueError( - _('Unable to find any valid segment in given context.')) + raise ValueError('Unable to find any valid segment in given context.') def to_dict(self): data = super(OvsdbNetworkTopologyElement, self).to_dict() diff --git a/networking-odl/networking_odl/ml2/pseudo_agentdb_binding.py b/networking-odl/networking_odl/ml2/pseudo_agentdb_binding.py deleted file mode 100644 index d24bd55..0000000 --- a/networking-odl/networking_odl/ml2/pseudo_agentdb_binding.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright (c) 2016 OpenStack Foundation -# 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. - -import logging -from neutron_lib import constants as nl_const -from requests import exceptions -import six.moves.urllib.parse as urlparse -from string import Template - -from oslo_config import cfg -from oslo_log import log -from oslo_serialization import jsonutils - -from neutron import context -from neutron.extensions import portbindings -from neutron import manager -from neutron.plugins.ml2 import driver_api - -from networking_odl._i18n import _LE, _LI, _LW -from networking_odl.common import client as odl_client -from networking_odl.journal import maintenance as mt -from networking_odl.ml2 import port_binding - -cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') -LOG = log.getLogger(__name__) - - -class PseudoAgentDBBindingController(port_binding.PortBindingController): - """Switch agnostic Port binding controller for OpenDayLight.""" - - AGENTDB_BINARY = 'neutron-odlagent-portbinding' - L2_TYPE = "ODL L2" - - # TODO(mzmalick): binary, topic and resource_versions to be provided - # by ODL, Pending ODL NB patches. - agentdb_row = { - 'binary': AGENTDB_BINARY, - 'host': '', - 'topic': nl_const.L2_AGENT_TOPIC, - 'configurations': {}, - 'resource_versions': '', - 'agent_type': L2_TYPE, - 'start_flag': True} - # We are not running host agents, so above start_flag is redundant - - def __init__(self, hostconf_uri=None, db_plugin=None): - """Initialization.""" - LOG.debug("Initializing ODL Port Binding Controller") - - if not hostconf_uri: - # extract host/port from ODL URL and append hostconf_uri path - hostconf_uri = self._make_hostconf_uri( - cfg.CONF.ml2_odl.url, cfg.CONF.ml2_odl.odl_hostconf_uri) - - LOG.debug("ODLPORTBINDING hostconfigs URI: %s", hostconf_uri) - - # TODO(mzmalick): disable port-binding for ODL lightweight testing - self.odl_rest_client = odl_client.OpenDaylightRestClient.create_client( - url=hostconf_uri) - - # Neutron DB plugin instance - self.agents_db = db_plugin - - # Start polling ODL restconf using maintenance thread. - # default: 30s (should be <= agent keep-alive poll interval) - self._start_maintenance_thread(cfg.CONF.ml2_odl.restconf_poll_interval) - - def _make_hostconf_uri(self, odl_url=None, path=''): - """Make ODL hostconfigs URI with host/port extraced from ODL_URL.""" - # NOTE(yamahata): for unit test. - odl_url = odl_url or 'http://localhost:8080/' - - # extract ODL_IP and ODL_PORT from ODL_ENDPOINT and append path - # urlsplit and urlunparse don't throw exceptions - purl = urlparse.urlsplit(odl_url) - return urlparse.urlunparse((purl.scheme, purl.netloc, - path, '', '', '')) - # - # TODO(mzmalick): - # 1. implement websockets for ODL hostconfig events - # - - def _start_maintenance_thread(self, poll_interval): - self._mainth = mt.MaintenanceThread() - self._mainth.maintenance_interval = poll_interval - self._mainth.register_operation(self._get_and_update_hostconfigs) - self._mainth.start() - - def _rest_get_hostconfigs(self): - try: - response = self.odl_rest_client.get() - response.raise_for_status() - hostconfigs = response.json()['hostconfigs']['hostconfig'] - except exceptions.ConnectionError: - LOG.error(_LE("Cannot connect to the Opendaylight Controller"), - exc_info=True) - return None - except KeyError: - LOG.error(_LE("got invalid hostconfigs"), - exc_info=True) - return None - except Exception: - LOG.warning(_LW("REST/GET odl hostconfig failed, "), - exc_info=True) - return None - else: - if LOG.isEnabledFor(logging.DEBUG): - _hconfig_str = jsonutils.dumps( - response, sort_keys=True, indent=4, separators=(',', ': ')) - LOG.debug("ODLPORTBINDING hostconfigs:\n%s", _hconfig_str) - - return hostconfigs - - def _get_and_update_hostconfigs(self, session=None): - LOG.info(_LI("REST/GET hostconfigs from ODL")) - - hostconfigs = self._rest_get_hostconfigs() - - if not hostconfigs: - LOG.warning(_LW("ODL hostconfigs REST/GET failed, " - "will retry on next poll")) - return # retry on next poll - - self._update_agents_db(hostconfigs=hostconfigs) - - def _get_neutron_db_plugin(self): - if (not self.agents_db) and manager.NeutronManager.has_instance(): - self.agents_db = manager.NeutronManager.get_plugin() - return self.agents_db - - def _update_agents_db(self, hostconfigs): - LOG.debug("ODLPORTBINDING Updating agents DB with ODL hostconfigs") - - agents_db = self._get_neutron_db_plugin() - - if not agents_db: # if ML2 is still initializing - LOG.warning(_LW("ML2 still initializing, Will retry agentdb" - " update on next poll")) - return # Retry on next poll - - for host_config in hostconfigs: - try: - self.agentdb_row['host'] = host_config['host-id'] - self.agentdb_row['agent_type'] = host_config['host-type'] - self.agentdb_row['configurations'] = host_config['config'] - - agents_db.create_or_update_agent( - context.get_admin_context(), self.agentdb_row) - except Exception: - LOG.exception(_LE("Unable to update agentdb.")) - continue # try next hostcofig - - def _substitute_hconfig_tmpl(self, port_context, hconfig): - # TODO(mzmalick): Explore options for inlines string splicing of - # port-id to 14 bytes as required by vhostuser types - subs_ids = { - # $IDENTIFER string substitution in hostconfigs JSON string - 'PORT_ID': port_context.current['id'][:14] - } - - # Substitute identifiers and Convert JSON string to dict - hconfig_conf_json = Template(hconfig['configurations']) - substituted_str = hconfig_conf_json.safe_substitute(subs_ids) - hconfig['configurations'] = jsonutils.loads(substituted_str) - - return hconfig - - def bind_port(self, port_context): - """bind port using ODL host configuration.""" - # Get all ODL hostconfigs for this host and type - agentdb = port_context.host_agents(self.L2_TYPE) - - if not agentdb: - LOG.warning(_LW("No valid hostconfigs in agentsdb for host %s"), - port_context.host) - return - - for raw_hconfig in agentdb: - # do any $identifier substitution - hconfig = self._substitute_hconfig_tmpl(port_context, raw_hconfig) - - # Found ODL hostconfig for this host in agentdb - LOG.debug("ODLPORTBINDING bind port with hostconfig: %s", hconfig) - - if self._hconfig_bind_port(port_context, hconfig): - break # Port binding suceeded! - else: # Port binding failed! - LOG.warning(_LW("Failed to bind Port %(pid)s for host " - "%(host)s on network %(network)s."), { - 'pid': port_context.current['id'], - 'host': port_context.host, - 'network': port_context.network.current['id']}) - else: # No hostconfig found for host in agentdb. - LOG.warning(_LW("No ODL hostconfigs for host %s found in agentdb"), - port_context.host) - - def _hconfig_bind_port(self, port_context, hconfig): - """bind port after validating odl host configuration.""" - valid_segment = None - - for segment in port_context.segments_to_bind: - if self._is_valid_segment(segment, hconfig['configurations']): - valid_segment = segment - break - else: - LOG.debug("No valid segments found!") - return False - - confs = hconfig['configurations']['supported_vnic_types'] - - # nova provides vnic_type in port_context to neutron. - # neutron provides supported vif_type for binding based on vnic_type - # in this case ODL hostconfigs has the vif_type to bind for vnic_type - vnic_type = port_context.current.get(portbindings.VNIC_TYPE) - - if vnic_type != portbindings.VNIC_NORMAL: - LOG.error(_LE("Binding failed: unsupported VNIC %s"), vnic_type) - return False - - for conf in confs: - if conf["vnic_type"] == vnic_type: - vif_type = conf.get('vif_type', portbindings.VIF_TYPE_OVS) - LOG.debug("Binding vnic:'%s' to vif:'%s'", vnic_type, vif_type) - break - else: - vif_type = portbindings.VIF_TYPE_OVS # default: OVS - LOG.warning(_LW("No supported vif type found for host %s!, " - "defaulting to OVS"), port_context.host) - - vif_details = conf.get('vif_details', {}) - - if not vif_details: # empty vif_details could be trouble, warn. - LOG.warning(_LW("hostconfig:vif_details was empty!")) - - LOG.debug("Bind port %(port)s on network %(network)s with valid " - "segment %(segment)s and VIF type %(vif_type)r " - "VIF details %(vif_details)r.", - {'port': port_context.current['id'], - 'network': port_context.network.current['id'], - 'segment': valid_segment, 'vif_type': vif_type, - 'vif_details': vif_details}) - - port_context.set_binding(valid_segment[driver_api.ID], vif_type, - vif_details, - status=nl_const.PORT_STATUS_ACTIVE) - return True - - def _is_valid_segment(self, segment, conf): - """Verify a segment is supported by ODL.""" - network_type = segment[driver_api.NETWORK_TYPE] - return network_type in conf['allowed_network_types'] diff --git a/networking-odl/networking_odl/ml2/vpp_ml2.tar b/networking-odl/networking_odl/ml2/vpp_ml2.tar Binary files differnew file mode 100644 index 0000000..e181208 --- /dev/null +++ b/networking-odl/networking_odl/ml2/vpp_ml2.tar diff --git a/networking-odl/networking_odl/ml2/vpp_topology.py b/networking-odl/networking_odl/ml2/vpp_topology.py new file mode 100644 index 0000000..c16399d --- /dev/null +++ b/networking-odl/networking_odl/ml2/vpp_topology.py @@ -0,0 +1,194 @@ +# Copyright (c) 2016 OpenStack Foundation +# 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. + + +import collections +import os + +from oslo_log import log +import six + +from neutron.common import constants as n_const +from neutron.extensions import portbindings +from neutron.plugins.common import constants +from neutron.plugins.ml2 import driver_api + +from networking_odl.ml2 import network_topology + +LOG = log.getLogger(__name__) +HC_VPP_CAPABILITY = 'urn:opendaylight:params:xml:ns:yang:v3po' + + +class VppNetworkTopologyParser(network_topology.NetworkTopologyParser): + def new_element(self, devname): + return VppNetworkTopologyElement(devname=devname) + + def parse_network_topology(self, network_topologies): + LOG.debug("Parsing Topology using VPP Topology Parser") + elements_by_name = collections.OrderedDict() + for topology in network_topologies['network-topology']['topology']: + if topology['topology-id'].startswith('topology-netconf'): + for node in topology['node']: + # expected : + # "node-id": "name", + # "netconf-node-topology:host": "172.21.174.41" + # "netconf-node-topology:available-capabilities": { + # "available-capability" : contains the v3po model + node_name = node['node-id'] + LOG.debug("Examining capabilities for node: %s\n", + node_name) + try: + capabilities = node[ + 'netconf-node-topology:available-capabilities'] + LOG.debug("Node's capabilities: %s\n", + capabilities) + for item in capabilities['available-capability']: + if HC_VPP_CAPABILITY in item: + LOG.debug("Found VPP matching capability for " + "node: %s\n", node_name) + element = elements_by_name.get(node_name) + if element is None: + elements_by_name[node_name] = element = \ + self.new_element(node_name) + + self._update_elmnt_from_json_netconf_topo_node( + node, element, node_name) + except KeyError: + LOG.debug("No netconf available capabilities found for" + ": %s\n", node_name) + + # Can there can be more VPP instances connected beside the same IP + # address? + # Cache will yield more instaces for the same key + for __, element in six.iteritems(elements_by_name): + yield element + + def _update_elmnt_from_json_netconf_topo_node( + self, node, element, node_name): + + # fetch remote IP address + element.remote_ip = node["netconf-node-topology:host"] + # Assume Honeycomb-VPP supports vhost_user + element.support_vhost_user = True + + LOG.debug( + 'Topology element updated:\n' + ' - VPP node name: %(node_name)r\n' + ' - remote_ip: %(remote_ip)r\n' + ' - support_vhost_user: %(support_vhost_user)r', + {'node_name': node_name, + 'remote_ip': element.remote_ip, + 'support_vhost_user': element.support_vhost_user}) + + +class VppNetworkTopologyElement(network_topology.NetworkTopologyElement): + devname = None # Filled in by parser + remote_ip = None # Filled in by parser + has_datapath_type_netdev = False # Placeholder for future capability + support_vhost_user = False # VPP supports it by default actually. + + # location for vhostuser sockets. + # TODO(wdec): This should be configurable in the ML2 config. + vhostuser_socket_dir = '/tmp/' + + # TODO(wdec): And also this should be configurable in ML2... + # prefix for port + port_prefix = 'socket_' + + def __init__(self, **kwargs): + for name, value in six.iteritems(kwargs): + setattr(self, name, value) + + @property + def host_addresses(self): + # For now it support only the remote IP found in connection info + return self.remote_ip, + + @property + def valid_vif_types(self): + return [portbindings.VIF_TYPE_VHOST_USER] + + def bind_port(self, port_context, vif_type, vif_details): + + port_context_id = port_context.current['id'] + network_context_id = port_context.network.current['id'] + + # Bind port to the first valid segment + for segment in port_context.segments_to_bind: + if self._is_valid_segment(segment): + # Guest best VIF type for given host + vif_details = self._get_vif_details( + vif_details=vif_details, port_context_id=port_context_id, + vif_type=vif_type) + LOG.debug( + 'Bind port with valid segment:\n' + '\tport: %(port)r\n' + '\tnetwork: %(network)r\n' + '\tsegment: %(segment)r\n' + '\tVIF type: %(vif_type)r\n' + '\tVIF details: %(vif_details)r', + {'port': port_context_id, + 'network': network_context_id, + 'segment': segment, 'vif_type': vif_type, + 'vif_details': vif_details}) + port_context.set_binding( + segment[driver_api.ID], vif_type, vif_details, + status=n_const.PORT_STATUS_ACTIVE) + return + + raise ValueError('Unable to find any valid segment in given context.') + + def to_dict(self): + data = super(VppNetworkTopologyElement, self).to_dict() + data.update( + {'uuid': self.devname, + 'has_datapath_type_netdev': self.has_datapath_type_netdev, + 'support_vhost_user': self.support_vhost_user, + 'valid_vif_types': self.valid_vif_types}) + if portbindings.VIF_TYPE_VHOST_USER in self.valid_vif_types: + data.update({'port_prefix': self.port_prefix, + 'vhostuser_socket_dir': self.vhostuser_socket_dir}) + return data + + def _is_valid_segment(self, segment): + """Verify a segment is valid for the OpenDaylight MechanismDriver. + + Verify the requested segment is supported by ODL and return True or + False to indicate this to callers. + """ + + network_type = segment[driver_api.NETWORK_TYPE] + return network_type in [constants.TYPE_LOCAL, constants.TYPE_GRE, + constants.TYPE_VXLAN, constants.TYPE_VLAN, + constants.TYPE_FLAT] + + def _get_vif_details(self, vif_details, port_context_id, vif_type): + vif_details = dict(vif_details) + if vif_type == portbindings.VIF_TYPE_VHOST_USER: + socket_path = os.path.join( + self.vhostuser_socket_dir, + (self.port_prefix + port_context_id)) + + vif_details.update({ + portbindings.VHOST_USER_MODE: + portbindings.VHOST_USER_MODE_SERVER, + portbindings.VHOST_USER_SOCKET: socket_path + }) + return vif_details + + def __setattr__(self, name, value): + # raises Attribute error if the class hasn't this attribute + getattr(type(self), name) + super(VppNetworkTopologyElement, self).__setattr__(name, value) diff --git a/networking-odl/networking_odl/tests/unit/common/test_callback.py b/networking-odl/networking_odl/tests/unit/common/test_callback.py index f5e2ee6..b7720b3 100644 --- a/networking-odl/networking_odl/tests/unit/common/test_callback.py +++ b/networking-odl/networking_odl/tests/unit/common/test_callback.py @@ -18,6 +18,7 @@ from networking_odl.common import constants as odl_const from networking_odl.ml2.mech_driver import OpenDaylightDriver import mock +import testscenarios import testtools from neutron.callbacks import events @@ -27,57 +28,110 @@ from neutron.callbacks import resources FAKE_ID = 'fakeid' -class ODLCallbackTestCase(testtools.TestCase): - odl_driver = OpenDaylightDriver() - sgh = callback.OdlSecurityGroupsHandler(odl_driver) +class ODLCallbackTestCase(testscenarios.WithScenarios, testtools.TestCase): + odl_client = OpenDaylightDriver() + scenarios = [ + ('after', { + 'sgh': callback.OdlSecurityGroupsHandler(odl_client, + "AFTER")}), + ('precommit', { + 'sgh': callback.OdlSecurityGroupsHandler(odl_client, + "PRECOMMIT")}), + ] def setUp(self): super(ODLCallbackTestCase, self).setUp() @mock.patch.object(OpenDaylightDriver, 'sync_from_callback') - def _test_callback_for_sg(self, event, op, sg, sg_id, sfc): + def test_callback_sg_create(self, sfc): + context = mock.Mock() + sg = mock.Mock() + default_sg = mock.Mock() + kwargs = { + 'context': context, + 'security_group': sg, + 'security_groups': odl_const.ODL_SGS, + 'is_default': default_sg, + } self.sgh.sg_callback(resources.SECURITY_GROUP, - event, - None, - security_group=sg, - security_group_id=sg_id) + events.AFTER_CREATE, + "trigger", + **kwargs) - expected_dict = ({resources.SECURITY_GROUP: sg} - if sg is not None else None) - sfc.assert_called_with( - op, callback._RESOURCE_MAPPING[resources.SECURITY_GROUP], sg_id, - expected_dict) + sfc.assert_called_with(odl_const.ODL_CREATE, + 'security-groups', + None, {'security_group': sg}) - def test_callback_sg_create(self): - self._test_callback_for_sg(events.AFTER_CREATE, odl_const.ODL_CREATE, - mock.Mock(), None) + @mock.patch.object(OpenDaylightDriver, 'sync_from_callback') + def test_callback_sg_update(self, sfc): + context = mock.Mock() + sg = mock.Mock() + kwargs = { + 'context': context, + 'security_group_id': FAKE_ID, + 'security_group': sg, + 'security_groups': odl_const.ODL_SGS, + } + self.sgh.sg_callback(resources.SECURITY_GROUP, + events.AFTER_UPDATE, + "trigger", + **kwargs) + + sfc.assert_called_with(odl_const.ODL_UPDATE, + 'security-groups', + FAKE_ID, {'security_group': sg}) + + @mock.patch.object(OpenDaylightDriver, 'sync_from_callback') + def test_callback_sg_delete(self, sfc): + context = mock.Mock() + sg = mock.Mock() + kwargs = { + 'context': context, + 'security_group_id': FAKE_ID, + 'security_group': sg, + 'security_groups': odl_const.ODL_SGS, + } + self.sgh.sg_callback(resources.SECURITY_GROUP, + events.AFTER_DELETE, + "trigger", + **kwargs) - def test_callback_sg_update(self): - self._test_callback_for_sg(events.AFTER_UPDATE, odl_const.ODL_UPDATE, - mock.Mock(), FAKE_ID) + sfc.assert_called_with(odl_const.ODL_DELETE, + 'security-groups', + FAKE_ID, {'security_group': sg}) - def test_callback_sg_delete(self): - self._test_callback_for_sg(events.AFTER_DELETE, odl_const.ODL_DELETE, - None, FAKE_ID) + @mock.patch.object(OpenDaylightDriver, 'sync_from_callback') + def test_callback_sg_rules_create(self, sfc): + context = mock.Mock() + security_group_rule = mock.Mock() + kwargs = { + 'context': context, + 'security_group_rule': security_group_rule, + 'security_group_rules': odl_const.ODL_SG_RULES, + } + self.sgh.sg_callback(resources.SECURITY_GROUP_RULE, + events.AFTER_CREATE, + "trigger", + **kwargs) + + sfc.assert_called_with(odl_const.ODL_CREATE, + 'security-group-rules', + None, + {'security_group_rule': security_group_rule}) @mock.patch.object(OpenDaylightDriver, 'sync_from_callback') - def _test_callback_for_sg_rules(self, event, op, sg_rule, sg_rule_id, sfc): + def test_callback_sg_rules_delete(self, sfc): + context = mock.Mock() + kwargs = { + 'context': context, + 'security_group_rule_id': FAKE_ID, + 'security_group_rules': odl_const.ODL_SG_RULES, + } self.sgh.sg_callback(resources.SECURITY_GROUP_RULE, - event, - None, - security_group_rule=sg_rule, - security_group_rule_id=sg_rule_id) - - expected_dict = ({resources.SECURITY_GROUP_RULE: sg_rule} - if sg_rule is not None else None) - sfc.assert_called_with( - op, callback._RESOURCE_MAPPING[resources.SECURITY_GROUP_RULE], - sg_rule_id, expected_dict) - - def test_callback_sg_rules_create(self): - self._test_callback_for_sg_rules( - events.AFTER_CREATE, odl_const.ODL_CREATE, mock.Mock(), None) - - def test_callback_sg_rules_delete(self): - self._test_callback_for_sg_rules( - events.AFTER_DELETE, odl_const.ODL_DELETE, None, FAKE_ID) + events.AFTER_DELETE, + "trigger", + **kwargs) + + sfc.assert_called_with(odl_const.ODL_DELETE, + 'security-group-rules', + FAKE_ID, None) diff --git a/networking-odl/networking_odl/tests/unit/common/test_lightweight_testing.py b/networking-odl/networking_odl/tests/unit/common/test_lightweight_testing.py index ea3b5a8..3fa63fa 100644 --- a/networking-odl/networking_odl/tests/unit/common/test_lightweight_testing.py +++ b/networking-odl/networking_odl/tests/unit/common/test_lightweight_testing.py @@ -152,7 +152,7 @@ class LightweightTestingTestCase(base.DietTestCase): self.assertEqual(lwt.NO_CONTENT, response.status_code) lwt_dict = lwt.OpenDaylightLwtClient.lwt_dict network = lwt_dict['networks'].get('fakeid1') - self.assertIsNone(network) + self.assertEqual(None, network) @mock.patch.dict(lwt.OpenDaylightLwtClient.lwt_dict, {'networks': {'fakeid1': {'id': 'fakeid1', @@ -169,6 +169,6 @@ class LightweightTestingTestCase(base.DietTestCase): self.assertEqual(lwt.NO_CONTENT, response.status_code) lwt_dict = lwt.OpenDaylightLwtClient.lwt_dict network = lwt_dict['networks'].get('fakeid1') - self.assertIsNone(network) + self.assertEqual(None, network) network = lwt_dict['networks'].get('fakeid2') - self.assertIsNone(network) + self.assertEqual(None, network) diff --git a/networking-odl/networking_odl/tests/unit/db/test_db.py b/networking-odl/networking_odl/tests/unit/db/test_db.py index 72749ad..9c03490 100644 --- a/networking-odl/networking_odl/tests/unit/db/test_db.py +++ b/networking-odl/networking_odl/tests/unit/db/test_db.py @@ -41,7 +41,6 @@ class DbTestCase(SqlTestCaseLight, TestCase): def _db_cleanup(self): self.db_session.query(models.OpendaylightJournal).delete() - self.db_session.query(models.OpendaylightMaintenance).delete() def _update_row(self, row): self.db_session.merge(row) @@ -162,33 +161,6 @@ class DbTestCase(SqlTestCaseLight, TestCase): self.assertEqual(2, update_mock.call_count) - def _test_delete_rows_by_state_and_time(self, last_retried, row_retention, - state, expected_rows): - db.create_pending_row(self.db_session, *self.UPDATE_ROW) - - # update state and last retried - row = db.get_all_db_rows(self.db_session)[0] - row.state = state - row.last_retried = row.last_retried - timedelta(seconds=last_retried) - self._update_row(row) - - db.delete_rows_by_state_and_time(self.db_session, - odl_const.COMPLETED, - timedelta(seconds=row_retention)) - - # validate the number of rows in the journal - rows = db.get_all_db_rows(self.db_session) - self.assertEqual(expected_rows, len(rows)) - - def test_delete_completed_rows_no_new_rows(self): - self._test_delete_rows_by_state_and_time(0, 10, odl_const.COMPLETED, 1) - - def test_delete_completed_rows_one_new_row(self): - self._test_delete_rows_by_state_and_time(6, 5, odl_const.COMPLETED, 0) - - def test_delete_completed_rows_wrong_state(self): - self._test_delete_rows_by_state_and_time(10, 8, odl_const.PENDING, 1) - def test_valid_retry_count(self): self._test_retry_count(1, 1, 1, odl_const.PENDING) @@ -206,38 +178,3 @@ class DbTestCase(SqlTestCaseLight, TestCase): def test_update_row_state_to_completed(self): self._test_update_row_state(odl_const.PROCESSING, odl_const.COMPLETED) - - def _test_maintenance_lock_unlock(self, db_func, existing_state, - expected_state, expected_result): - row = models.OpendaylightMaintenance(id='test', - state=existing_state) - self.db_session.add(row) - self.db_session.flush() - - self.assertEqual(expected_result, db_func(self.db_session)) - row = self.db_session.query(models.OpendaylightMaintenance).one() - self.assertEqual(expected_state, row['state']) - - def test_lock_maintenance(self): - self._test_maintenance_lock_unlock(db.lock_maintenance, - odl_const.PENDING, - odl_const.PROCESSING, - True) - - def test_lock_maintenance_fails_when_processing(self): - self._test_maintenance_lock_unlock(db.lock_maintenance, - odl_const.PROCESSING, - odl_const.PROCESSING, - False) - - def test_unlock_maintenance(self): - self._test_maintenance_lock_unlock(db.unlock_maintenance, - odl_const.PROCESSING, - odl_const.PENDING, - True) - - def test_unlock_maintenance_fails_when_pending(self): - self._test_maintenance_lock_unlock(db.unlock_maintenance, - odl_const.PENDING, - odl_const.PENDING, - False) diff --git a/networking-odl/networking_odl/tests/unit/journal/__init__.py b/networking-odl/networking_odl/tests/unit/journal/__init__.py deleted file mode 100644 index e69de29..0000000 --- a/networking-odl/networking_odl/tests/unit/journal/__init__.py +++ /dev/null diff --git a/networking-odl/networking_odl/tests/unit/journal/test_dependency_validations.py b/networking-odl/networking_odl/tests/unit/journal/test_dependency_validations.py deleted file mode 100644 index 39a4b98..0000000 --- a/networking-odl/networking_odl/tests/unit/journal/test_dependency_validations.py +++ /dev/null @@ -1,44 +0,0 @@ -# -# Copyright (C) 2016 Intel Corp. Isaku Yamahata <isaku.yamahata@gmail com> -# 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. -# - -import mock - -from neutron.tests import base - -from networking_odl.journal import dependency_validations - - -class DependencyValidationsTestCase(base.DietTestCase): - _RESOURCE_DUMMY = 'test_type' - - def setUp(self): - super(DependencyValidationsTestCase, self).setUp() - mock_validation_map = mock.patch.dict( - dependency_validations._VALIDATION_MAP) - mock_validation_map.start() - self.addCleanup(mock_validation_map.stop) - - def test_register_validator(self): - mock_session = mock.Mock() - mock_validator = mock.Mock(return_value=False) - mock_row = mock.Mock() - mock_row.object_type = self._RESOURCE_DUMMY - dependency_validations.register_validator(self._RESOURCE_DUMMY, - mock_validator) - valid = dependency_validations.validate(mock_session, mock_row) - mock_validator.assert_called_once_with(mock_session, mock_row) - self.assertFalse(valid) diff --git a/networking-odl/networking_odl/tests/unit/journal/test_full_sync.py b/networking-odl/networking_odl/tests/unit/journal/test_full_sync.py deleted file mode 100644 index cedccbd..0000000 --- a/networking-odl/networking_odl/tests/unit/journal/test_full_sync.py +++ /dev/null @@ -1,152 +0,0 @@ -# -# Copyright (C) 2016 Red Hat, Inc. -# -# 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 -import requests - -from neutron.db import api as neutron_db_api -from neutron import manager -from neutron.tests.unit.testlib_api import SqlTestCaseLight - -from networking_odl.common import constants as odl_const -from networking_odl.db import db -from networking_odl.db import models -from networking_odl.journal import full_sync - - -class FullSyncTestCase(SqlTestCaseLight): - def setUp(self): - super(FullSyncTestCase, self).setUp() - self.db_session = neutron_db_api.get_session() - - full_sync._CLIENT = mock.MagicMock() - self.plugin_mock = mock.patch.object(manager.NeutronManager, - 'get_plugin').start() - self.l3_plugin_mock = mock.patch.object(manager.NeutronManager, - 'get_service_plugins').start() - - self.addCleanup(self._db_cleanup) - - def _db_cleanup(self): - self.db_session.query(models.OpendaylightJournal).delete() - - def test_no_full_sync_when_canary_exists(self): - full_sync.full_sync(self.db_session) - self.assertEqual([], db.get_all_db_rows(self.db_session)) - - def _mock_l2_resources(self): - expected_journal = {odl_const.ODL_NETWORK: '1', - odl_const.ODL_SUBNET: '2', - odl_const.ODL_PORT: '3'} - plugin_instance = self.plugin_mock.return_value - plugin_instance.get_networks.return_value = [ - {'id': expected_journal[odl_const.ODL_NETWORK]}] - plugin_instance.get_subnets.return_value = [ - {'id': expected_journal[odl_const.ODL_SUBNET]}] - plugin_instance.get_ports.side_effect = ([ - {'id': expected_journal[odl_const.ODL_PORT]}], []) - return expected_journal - - def _filter_out_canary(self, rows): - return [row for row in rows if row['object_uuid'] != - full_sync._CANARY_NETWORK_ID] - - def _test_no_full_sync_when_canary_in_journal(self, state): - self._mock_canary_missing() - self._mock_l2_resources() - db.create_pending_row(self.db_session, odl_const.ODL_NETWORK, - full_sync._CANARY_NETWORK_ID, - odl_const.ODL_CREATE, {}) - row = db.get_all_db_rows(self.db_session)[0] - db.update_db_row_state(self.db_session, row, state) - - full_sync.full_sync(self.db_session) - - rows = db.get_all_db_rows(self.db_session) - self.assertEqual([], self._filter_out_canary(rows)) - - def test_no_full_sync_when_canary_pending_creation(self): - self._test_no_full_sync_when_canary_in_journal(odl_const.PENDING) - - def test_no_full_sync_when_canary_is_processing(self): - self._test_no_full_sync_when_canary_in_journal(odl_const.PROCESSING) - - def test_client_error_propagates(self): - class TestException(Exception): - def __init__(self): - pass - - full_sync._CLIENT.get.side_effect = TestException() - self.assertRaises(TestException, full_sync.full_sync, self.db_session) - - def _mock_canary_missing(self): - get_return = mock.MagicMock() - get_return.status_code = requests.codes.not_found - full_sync._CLIENT.get.return_value = get_return - - def _assert_canary_created(self): - rows = db.get_all_db_rows(self.db_session) - self.assertTrue(any(r['object_uuid'] == full_sync._CANARY_NETWORK_ID - for r in rows)) - return rows - - def _test_full_sync_resources(self, expected_journal): - self._mock_canary_missing() - - full_sync.full_sync(self.db_session) - - rows = self._assert_canary_created() - rows = self._filter_out_canary(rows) - self.assertItemsEqual(expected_journal.keys(), - [row['object_type'] for row in rows]) - for row in rows: - self.assertEqual(expected_journal[row['object_type']], - row['object_uuid']) - - def test_full_sync_removes_pending_rows(self): - db.create_pending_row(self.db_session, odl_const.ODL_NETWORK, "uuid", - odl_const.ODL_CREATE, {'foo': 'bar'}) - self._test_full_sync_resources({}) - - def test_full_sync_no_resources(self): - self._test_full_sync_resources({}) - - def test_full_sync_l2_resources(self): - self._test_full_sync_resources(self._mock_l2_resources()) - - def _mock_router_port(self, port_id): - router_port = {'id': port_id, - 'device_id': '1', - 'tenant_id': '1', - 'fixed_ips': [{'subnet_id': '1'}]} - plugin_instance = self.plugin_mock.return_value - plugin_instance.get_ports.side_effect = ([], [router_port]) - - def _mock_l3_resources(self): - expected_journal = {odl_const.ODL_ROUTER: '1', - odl_const.ODL_FLOATINGIP: '2', - odl_const.ODL_ROUTER_INTF: '3'} - plugin_instance = self.l3_plugin_mock.return_value.get.return_value - plugin_instance.get_routers.return_value = [ - {'id': expected_journal[odl_const.ODL_ROUTER]}] - plugin_instance.get_floatingips.return_value = [ - {'id': expected_journal[odl_const.ODL_FLOATINGIP]}] - self._mock_router_port(expected_journal[odl_const.ODL_ROUTER_INTF]) - - return expected_journal - - def test_full_sync_l3_resources(self): - self._test_full_sync_resources(self._mock_l3_resources()) diff --git a/networking-odl/networking_odl/tests/unit/journal/test_maintenance.py b/networking-odl/networking_odl/tests/unit/journal/test_maintenance.py deleted file mode 100644 index eb823cd..0000000 --- a/networking-odl/networking_odl/tests/unit/journal/test_maintenance.py +++ /dev/null @@ -1,93 +0,0 @@ -# -# Copyright (C) 2016 Red Hat, Inc. -# -# 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 -import threading -from unittest2.case import TestCase - -from neutron.db import api as neutron_db_api -from neutron.tests.unit.testlib_api import SqlTestCaseLight - -from networking_odl.common import constants as odl_const -from networking_odl.db import models -from networking_odl.journal import maintenance - - -class MaintenanceThreadTestCase(SqlTestCaseLight, TestCase): - def setUp(self): - super(MaintenanceThreadTestCase, self).setUp() - self.db_session = neutron_db_api.get_session() - - row = models.OpendaylightMaintenance(state=odl_const.PENDING) - self.db_session.add(row) - self.db_session.flush() - - self.thread = maintenance.MaintenanceThread() - self.thread.maintenance_interval = 0.01 - - def test__execute_op_no_exception(self): - with mock.patch.object(maintenance, 'LOG') as mock_log: - operation = mock.MagicMock() - operation.__name__ = "test" - self.thread._execute_op(operation, self.db_session) - self.assertTrue(operation.called) - self.assertTrue(mock_log.info.called) - self.assertFalse(mock_log.exception.called) - - def test__execute_op_with_exception(self): - with mock.patch.object(maintenance, 'LOG') as mock_log: - operation = mock.MagicMock(side_effect=Exception()) - operation.__name__ = "test" - self.thread._execute_op(operation, self.db_session) - self.assertTrue(mock_log.exception.called) - - def test_thread_works(self): - callback_event = threading.Event() - count = [0] - - def callback_op(**kwargs): - count[0] += 1 - - # The following should be true on the second call, so we're making - # sure that the thread runs more than once. - if count[0] > 1: - callback_event.set() - - self.thread.register_operation(callback_op) - self.thread.start() - - # Make sure the callback event was called and not timed out - self.assertTrue(callback_event.wait(timeout=5)) - - def test_thread_continues_after_exception(self): - exception_event = threading.Event() - callback_event = threading.Event() - - def exception_op(**kwargs): - if not exception_event.is_set(): - exception_event.set() - raise Exception() - - def callback_op(**kwargs): - callback_event.set() - - for op in [exception_op, callback_op]: - self.thread.register_operation(op) - - self.thread.start() - - # Make sure the callback event was called and not timed out - self.assertTrue(callback_event.wait(timeout=5)) diff --git a/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py index 232864d..77a874f 100644 --- a/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py +++ b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl.py @@ -22,7 +22,6 @@ import copy import mock from neutron.extensions import l3 -from neutron.extensions import l3_ext_gw_mode from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.extensions import base as test_extensions_base from webob import exc @@ -36,15 +35,11 @@ class Testodll3(test_extensions_base.ExtensionTestCase): def setUp(self): super(Testodll3, self).setUp() - # support ext-gw-mode - for key in l3.RESOURCE_ATTRIBUTE_MAP.keys(): - l3.RESOURCE_ATTRIBUTE_MAP[key].update( - l3_ext_gw_mode.EXTENDED_ATTRIBUTES_2_0.get(key, {})) self._setUpExtension( 'neutron.extensions.l3.RouterPluginBase', None, l3.RESOURCE_ATTRIBUTE_MAP, l3.L3, '', allow_pagination=True, allow_sorting=True, - supported_extension_aliases=['router', 'ext-gw-mode'], + supported_extension_aliases=['router'], use_quota=True) @staticmethod @@ -116,13 +111,13 @@ class Testodll3(test_extensions_base.ExtensionTestCase): content_type='application/%s' % self.fmt) instance.create_router.assert_called_once_with(mock.ANY, router=router) - self.assertEqual(exc.HTTPCreated.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPCreated.code) res = self.deserialize(res) self.assertIn('router', res) router = res['router'] - self.assertEqual(router_id, router['id']) - self.assertEqual("ACTIVE", router['status']) - self.assertEqual(True, router['admin_state_up']) + self.assertEqual(router['id'], router_id) + self.assertEqual(router['status'], "ACTIVE") + self.assertEqual(router['admin_state_up'], True) def test_update_router(self): router_id, router = self._get_router_test() @@ -144,14 +139,14 @@ class Testodll3(test_extensions_base.ExtensionTestCase): instance.update_router.assert_called_once_with(mock.ANY, router_id, router=router_request) - self.assertEqual(exc.HTTPOk.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('router', res) router = res['router'] - self.assertEqual(router_id, router['id']) - self.assertEqual("3c5bcddd-6af9-4e6b-9c3e-c153e521cab8", - router["external_gateway_info"]['network_id']) - self.assertEqual(True, router["external_gateway_info"]['enable_snat']) + self.assertEqual(router['id'], router_id) + self.assertEqual(router["external_gateway_info"]['network_id'], + "3c5bcddd-6af9-4e6b-9c3e-c153e521cab8") + self.assertEqual(router["external_gateway_info"]['enable_snat'], True) def test_delete_router(self): router_id, router = self._get_router_test() @@ -161,7 +156,7 @@ class Testodll3(test_extensions_base.ExtensionTestCase): res = self.api.delete(_get_path('routers', id=router_id, fmt=self.fmt)) instance.delete_router.assert_called_once_with(mock.ANY, router_id) - self.assertEqual(exc.HTTPNoContent.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPNoContent.code) def test_create_floating_ip(self): floating_ip_id, floating_ip = self._get_floating_ip_test() @@ -194,12 +189,12 @@ class Testodll3(test_extensions_base.ExtensionTestCase): assert_called_once_with(mock.ANY, floatingip=floating_ip_request) - self.assertEqual(exc.HTTPCreated.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPCreated.code) res = self.deserialize(res) self.assertIn('floatingip', res) floatingip = res['floatingip'] - self.assertEqual(floating_ip_id, floatingip['id']) - self.assertEqual("ACTIVE", floatingip['status']) + self.assertEqual(floatingip['id'], floating_ip_id) + self.assertEqual(floatingip['status'], "ACTIVE") def test_update_floating_ip(self): floating_ip_id, floating_ip = self._get_floating_ip_test() @@ -232,13 +227,13 @@ class Testodll3(test_extensions_base.ExtensionTestCase): floating_ip_id, floatingip=floating_ip_request) - self.assertEqual(exc.HTTPOk.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('floatingip', res) floatingip = res['floatingip'] - self.assertEqual(floating_ip_id, floatingip['id']) - self.assertIsNone(floatingip['port_id']) - self.assertIsNone(floatingip['fixed_ip_address']) + self.assertEqual(floatingip['id'], floating_ip_id) + self.assertEqual(floatingip['port_id'], None) + self.assertEqual(floatingip['fixed_ip_address'], None) def test_delete_floating_ip(self): floating_ip_id, floating_ip = self._get_floating_ip_test() @@ -250,7 +245,7 @@ class Testodll3(test_extensions_base.ExtensionTestCase): instance.delete_floatingip.assert_called_once_with(mock.ANY, floating_ip_id) - self.assertEqual(exc.HTTPNoContent.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPNoContent.code) def test_add_router_interface(self): router_id, router = self._get_router_test() @@ -274,11 +269,11 @@ class Testodll3(test_extensions_base.ExtensionTestCase): router_id, interface_info) - self.assertEqual(exc.HTTPOk.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) - self.assertEqual(router_id, res['id']) - self.assertEqual("a2f1f29d-571b-4533-907f-5803ab96ead1", - res['subnet_id']) + self.assertEqual(res['id'], router_id) + self.assertEqual(res['subnet_id'], + "a2f1f29d-571b-4533-907f-5803ab96ead1") def test_remove_router_interface(self): router_id, router = self._get_router_test() @@ -303,8 +298,8 @@ class Testodll3(test_extensions_base.ExtensionTestCase): router_id, interface_info) - self.assertEqual(exc.HTTPOk.code, res.status_int) + self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) - self.assertEqual(router_id, res['id']) - self.assertEqual("a2f1f29d-571b-4533-907f-5803ab96ead1", - res['subnet_id']) + self.assertEqual(res['id'], router_id) + self.assertEqual(res['subnet_id'], + "a2f1f29d-571b-4533-907f-5803ab96ead1") diff --git a/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py index da3f644..4f9061a 100644..100755 --- a/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py +++ b/networking-odl/networking_odl/tests/unit/l3/test_l3_odl_v2.py @@ -78,7 +78,10 @@ class DataMatcher(object): def __init__(self, operation, object_type, object_dict): self._data = object_dict.copy() self._object_type = object_type - filters.filter_for_odl(object_type, operation, self._data) + filter_cls = filters.FILTER_MAP[object_type] + attr_filter = getattr(filter_cls, + 'filter_' + operation + '_attributes') + attr_filter(self._data) def __eq__(self, s): data = jsonutils.loads(s) @@ -87,9 +90,6 @@ class DataMatcher(object): else: return self._data == data[self._object_type] - def __ne__(self, s): - return not self.__eq__(s) - class OpenDaylightL3TestCase(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, base.BaseTestCase): diff --git a/networking-odl/networking_odl/tests/unit/ml2/config-ovs-external_ids.sh b/networking-odl/networking_odl/tests/unit/ml2/config-ovs-external_ids.sh deleted file mode 100755 index 15f9b93..0000000 --- a/networking-odl/networking_odl/tests/unit/ml2/config-ovs-external_ids.sh +++ /dev/null @@ -1,37 +0,0 @@ -#!/bin/sh -# Copyright (c) 2016 OpenStack Foundation -# 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. - -uuid=$(sudo ovs-vsctl get Open_vSwitch . _uuid) - -# Test data -sudo ovs-vsctl set Open_vSwitch $uuid \ - external_ids:odl_os_hostconfig_hostid="devstack" - -# sudo ovs-vsctl set Open_vSwitch $uuid \ -# external_ids:odl_os_hostconfig_hosttype="ODL L2" - -config=$(cat <<____CONFIG -{"supported_vnic_types":[ - {"vnic_type":"normal","vif_type":"ovs","vif_details":{}}], - "allowed_network_types":["local","vlan","vxlan","gre"], - "bridge_mappings":{"physnet1":"br-ex"}} -____CONFIG -) - -echo config: $config - -sudo ovs-vsctl set Open_vSwitch $uuid \ - external_ids:odl_os_hostconfig_config_odl_l2="$config" diff --git a/networking-odl/networking_odl/tests/unit/ml2/odl_teststub.js b/networking-odl/networking_odl/tests/unit/ml2/odl_teststub.js deleted file mode 100644 index 1ee02d5..0000000 --- a/networking-odl/networking_odl/tests/unit/ml2/odl_teststub.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2016 OpenStack Foundation - * 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. - * - * $nodejs odl_teststub.js - * - * local.conf or ml2_conf.ini should be set to the following: - * - * [ml2_odl] - * port_binding_controller = pseudo-agentdb-binding - * password = admin - * username = admin - * url = http://localhost:8080/controller/nb/v2/neutron - * restconf_uri = http://localhost:8125/ # for this stub - * - * To test with ODL *end to end* use below URL for restconf_uri and configure - * ovsdb external_ids using the test script: config-ovs-external_ids.sh - * - * http://localhost:8181/restconf/operational/neutron:neutron/hostconfigs - */ - -var http = require('http'); - -const PORT=8125; - -__test_odl_hconfig = {"hostconfigs": {"hostconfig": [ - {"host-id": "devstack", - "host-type": "ODL L2", - "config": { - "supported_vnic_types": [ - {"vnic_type": "normal", - "vif_type": "ovs", - "vif_details": {}}], - "allowed_network_types": ["local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1":"br-ex"} - } - }] - }} - - -function handleRequest(req, res){ - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify(__test_odl_hconfig)); -} - -var server = http.createServer(handleRequest); - -server.listen(PORT, function(){ - console.log("Server listening on: http://localhost:%s", PORT); - }); diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_driver.py b/networking-odl/networking_odl/tests/unit/ml2/test_driver.py index 661eb55..e2ceda5 100644 --- a/networking-odl/networking_odl/tests/unit/ml2/test_driver.py +++ b/networking-odl/networking_odl/tests/unit/ml2/test_driver.py @@ -32,55 +32,55 @@ class TestODLShim(test_plugin.Ml2PluginV2TestCase): def test_create_network_postcommit(self): self.driver.create_network_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_CREATE, + self.driver.odl_drv.synchronize.assert_called_with('create', const.ODL_NETWORKS, self.context) def test_update_network_postcommit(self): self.driver.update_network_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_UPDATE, + self.driver.odl_drv.synchronize.assert_called_with('update', const.ODL_NETWORKS, self.context) def test_delete_network_postcommit(self): self.driver.delete_network_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_DELETE, + self.driver.odl_drv.synchronize.assert_called_with('delete', const.ODL_NETWORKS, self.context) def test_create_subnet_postcommit(self): self.driver.create_subnet_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_CREATE, + self.driver.odl_drv.synchronize.assert_called_with('create', const.ODL_SUBNETS, self.context) def test_update_subnet_postcommit(self): self.driver.update_subnet_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_UPDATE, + self.driver.odl_drv.synchronize.assert_called_with('update', const.ODL_SUBNETS, self.context) def test_delete_subnet_postcommit(self): self.driver.delete_subnet_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_DELETE, + self.driver.odl_drv.synchronize.assert_called_with('delete', const.ODL_SUBNETS, self.context) def test_create_port_postcommit(self): self.driver.create_port_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_CREATE, + self.driver.odl_drv.synchronize.assert_called_with('create', const.ODL_PORTS, self.context) def test_update_port_postcommit(self): self.driver.update_port_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_UPDATE, + self.driver.odl_drv.synchronize.assert_called_with('update', const.ODL_PORTS, self.context) def test_delete_port_postcommit(self): self.driver.delete_port_postcommit(self.context) - self.driver.odl_drv.synchronize.assert_called_with(const.ODL_DELETE, + self.driver.odl_drv.synchronize.assert_called_with('delete', const.ODL_PORTS, self.context) diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_legacy_port_binding.py b/networking-odl/networking_odl/tests/unit/ml2/test_legacy_port_binding.py index 932c961..8ebda22 100644 --- a/networking-odl/networking_odl/tests/unit/ml2/test_legacy_port_binding.py +++ b/networking-odl/networking_odl/tests/unit/ml2/test_legacy_port_binding.py @@ -15,11 +15,10 @@ import mock -from neutron.extensions import portbindings +from neutron.common import constants as n_constants from neutron.plugins.common import constants from neutron.plugins.ml2 import driver_api as api from neutron.plugins.ml2 import driver_context as ctx -from neutron_lib import constants as n_constants from networking_odl.ml2 import legacy_port_binding from networking_odl.tests import base @@ -74,16 +73,3 @@ class TestLegacyPortBindingManager(base.DietTestCase): port_context.set_binding.assert_called_once_with( self.valid_segment[api.ID], vif_type, mgr.vif_details, status=n_constants.PORT_STATUS_ACTIVE) - - def test_bind_port_unsupported_vnic_type(self): - network = mock.MagicMock(spec=api.NetworkContext) - port_context = mock.MagicMock( - spec=ctx.PortContext, - current={'id': 'CURRENT_CONTEXT_ID', - portbindings.VNIC_TYPE: portbindings.VNIC_DIRECT}, - segments_to_bind=[self.valid_segment, self.invalid_segment], - network=network) - - mgr = legacy_port_binding.LegacyPortBindingManager() - mgr.bind_port(port_context) - port_context.set_binding.assert_not_called() diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl.py b/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl.py index 95de10c..a012085 100644 --- a/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl.py +++ b/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl.py @@ -23,7 +23,7 @@ from oslo_serialization import jsonutils import requests import webob.exc -from neutron.db import segments_db +from neutron.common import constants as n_constants from neutron.extensions import portbindings from neutron.plugins.common import constants from neutron.plugins.ml2 import config as config @@ -33,7 +33,6 @@ from neutron.plugins.ml2 import plugin from neutron.tests import base from neutron.tests.unit.plugins.ml2 import test_plugin from neutron.tests.unit import testlib_api -from neutron_lib import constants as n_constants from networking_odl.common import client from networking_odl.common import constants as odl_const @@ -208,6 +207,12 @@ class OpenDaylightMechanismTestPortsV2(test_plugin.TestMl2PortsV2, expected_status=webob.exc.HTTPConflict.code, expected_error='PortBound') + def test_create_router_port_and_fail_create_postcommit(self): + # Skip this test case for now as a workaround. + # TODO(rzang): remove this once [1] gets in. + # [1]https://review.openstack.org/#/c/310682/ + self.skipTest("skip as a workaround") + class DataMatcher(object): @@ -223,9 +228,6 @@ class DataMatcher(object): data = jsonutils.loads(s) return self._data == data[self._object_type] - def __ne__(self, s): - return not self.__eq__(s) - class OpenDaylightSyncTestCase(OpenDaylightTestCase): @@ -376,8 +378,7 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): context = self._get_mock_operation_context(object_type) url = '%s/%ss' % (config.cfg.CONF.ml2_odl.url, object_type) kwargs = {'url': url, - 'data': DataMatcher(odl_const.ODL_CREATE, object_type, - context)} + 'data': DataMatcher('create', object_type, context)} self._test_single_operation(method, context, status_code, exc_class, 'post', **kwargs) @@ -388,8 +389,7 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): url = '%s/%ss/%s' % (config.cfg.CONF.ml2_odl.url, object_type, context.current['id']) kwargs = {'url': url, - 'data': DataMatcher(odl_const.ODL_UPDATE, object_type, - context)} + 'data': DataMatcher('update', object_type, context)} self._test_single_operation(method, context, status_code, exc_class, 'put', **kwargs) @@ -401,20 +401,18 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): context.current['id']) kwargs = {'url': url, 'data': None} self._test_single_operation(method, context, status_code, exc_class, - odl_const.ODL_DELETE, **kwargs) + 'delete', **kwargs) def test_create_network_postcommit(self): - self._test_create_resource_postcommit(odl_const.ODL_NETWORK, + self._test_create_resource_postcommit('network', requests.codes.created) for status_code in (requests.codes.bad_request, requests.codes.unauthorized): self._test_create_resource_postcommit( - odl_const.ODL_NETWORK, status_code, - requests.exceptions.HTTPError) + 'network', status_code, requests.exceptions.HTTPError) def test_create_subnet_postcommit(self): - self._test_create_resource_postcommit(odl_const.ODL_SUBNET, - requests.codes.created) + self._test_create_resource_postcommit('subnet', requests.codes.created) for status_code in (requests.codes.bad_request, requests.codes.unauthorized, requests.codes.forbidden, @@ -422,12 +420,10 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): requests.codes.conflict, requests.codes.not_implemented): self._test_create_resource_postcommit( - odl_const.ODL_SUBNET, status_code, - requests.exceptions.HTTPError) + 'subnet', status_code, requests.exceptions.HTTPError) def test_create_port_postcommit(self): - self._test_create_resource_postcommit(odl_const.ODL_PORT, - requests.codes.created) + self._test_create_resource_postcommit('port', requests.codes.created) for status_code in (requests.codes.bad_request, requests.codes.unauthorized, requests.codes.forbidden, @@ -436,34 +432,28 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): requests.codes.not_implemented, requests.codes.service_unavailable): self._test_create_resource_postcommit( - odl_const.ODL_PORT, status_code, - requests.exceptions.HTTPError) + 'port', status_code, requests.exceptions.HTTPError) def test_update_network_postcommit(self): - self._test_update_resource_postcommit(odl_const.ODL_NETWORK, - requests.codes.ok) + self._test_update_resource_postcommit('network', requests.codes.ok) for status_code in (requests.codes.bad_request, requests.codes.forbidden, requests.codes.not_found): self._test_update_resource_postcommit( - odl_const.ODL_NETWORK, status_code, - requests.exceptions.HTTPError) + 'network', status_code, requests.exceptions.HTTPError) def test_update_subnet_postcommit(self): - self._test_update_resource_postcommit(odl_const.ODL_SUBNET, - requests.codes.ok) + self._test_update_resource_postcommit('subnet', requests.codes.ok) for status_code in (requests.codes.bad_request, requests.codes.unauthorized, requests.codes.forbidden, requests.codes.not_found, requests.codes.not_implemented): self._test_update_resource_postcommit( - odl_const.ODL_SUBNET, status_code, - requests.exceptions.HTTPError) + 'subnet', status_code, requests.exceptions.HTTPError) def test_update_port_postcommit(self): - self._test_update_resource_postcommit(odl_const.ODL_PORT, - requests.codes.ok) + self._test_update_resource_postcommit('port', requests.codes.ok) for status_code in (requests.codes.bad_request, requests.codes.unauthorized, requests.codes.forbidden, @@ -471,55 +461,50 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): requests.codes.conflict, requests.codes.not_implemented): self._test_update_resource_postcommit( - odl_const.ODL_PORT, status_code, - requests.exceptions.HTTPError) + 'port', status_code, requests.exceptions.HTTPError) def test_delete_network_postcommit(self): - self._test_delete_resource_postcommit(odl_const.ODL_NETWORK, + self._test_delete_resource_postcommit('network', requests.codes.no_content) - self._test_delete_resource_postcommit(odl_const.ODL_NETWORK, + self._test_delete_resource_postcommit('network', requests.codes.not_found) for status_code in (requests.codes.unauthorized, requests.codes.conflict): self._test_delete_resource_postcommit( - odl_const.ODL_NETWORK, status_code, - requests.exceptions.HTTPError) + 'network', status_code, requests.exceptions.HTTPError) def test_delete_subnet_postcommit(self): - self._test_delete_resource_postcommit(odl_const.ODL_SUBNET, + self._test_delete_resource_postcommit('subnet', requests.codes.no_content) - self._test_delete_resource_postcommit(odl_const.ODL_SUBNET, + self._test_delete_resource_postcommit('subnet', requests.codes.not_found) for status_code in (requests.codes.unauthorized, requests.codes.conflict, requests.codes.not_implemented): self._test_delete_resource_postcommit( - odl_const.ODL_SUBNET, status_code, - requests.exceptions.HTTPError) + 'subnet', status_code, requests.exceptions.HTTPError) def test_delete_port_postcommit(self): - self._test_delete_resource_postcommit(odl_const.ODL_PORT, + self._test_delete_resource_postcommit('port', requests.codes.no_content) - self._test_delete_resource_postcommit(odl_const.ODL_PORT, + self._test_delete_resource_postcommit('port', requests.codes.not_found) for status_code in (requests.codes.unauthorized, requests.codes.forbidden, requests.codes.not_implemented): self._test_delete_resource_postcommit( - odl_const.ODL_PORT, status_code, - requests.exceptions.HTTPError) + 'port', status_code, requests.exceptions.HTTPError) def test_port_emtpy_tenant_id_work_around(self): """Validate the work around code of port creation""" plugin = mock.Mock() plugin_context = mock.Mock() - network = self._get_mock_operation_context( - odl_const.ODL_NETWORK).current - port = self._get_mock_operation_context(odl_const.ODL_PORT).current + network = self._get_mock_operation_context('network').current + port = self._get_mock_operation_context('port').current tenant_id = network['tenant_id'] port['tenant_id'] = '' - with mock.patch.object(segments_db, 'get_network_segments'): + with mock.patch.object(driver_context.db, 'get_network_segments'): context = driver_context.PortContext( plugin, plugin_context, port, network, {}, 0, None) self.mech.odl_drv.FILTER_MAP[ @@ -530,16 +515,15 @@ class OpenDaylightMechanismDriverTestCase(base.BaseTestCase): """Validate the filter code on update port operation""" items_to_filter = ['network_id', 'id', 'status', 'tenant_id'] plugin_context = mock.Mock() - network = self._get_mock_operation_context( - odl_const.ODL_NETWORK).current - subnet = self._get_mock_operation_context(odl_const.ODL_SUBNET).current - port = self._get_mock_operation_context(odl_const.ODL_PORT).current + network = self._get_mock_operation_context('network').current + subnet = self._get_mock_operation_context('subnet').current + port = self._get_mock_operation_context('port').current port['fixed_ips'] = [{'subnet_id': subnet['id'], 'ip_address': '10.0.0.10'}] port['mac_address'] = port['mac_address'].upper() orig_port = copy.deepcopy(port) - with mock.patch.object(segments_db, 'get_network_segments'): + with mock.patch.object(driver_context.db, 'get_network_segments'): context = driver_context.PortContext( plugin, plugin_context, port, network, {}, 0, None) self.mech.odl_drv.FILTER_MAP[ diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl_v2.py b/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl_v2.py index 7e8c7fc..08cf653 100644 --- a/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl_v2.py +++ b/networking-odl/networking_odl/tests/unit/ml2/test_mechanism_odl_v2.py @@ -12,14 +12,12 @@ # 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 datetime +from datetime import timedelta -from networking_odl.common import callback from networking_odl.common import client from networking_odl.common import constants as odl_const from networking_odl.common import filters from networking_odl.db import db -from networking_odl.journal import cleanup from networking_odl.journal import journal from networking_odl.ml2 import mech_driver_v2 @@ -29,7 +27,6 @@ from oslo_serialization import jsonutils import requests from neutron.db import api as neutron_db_api -from neutron import manager from neutron.plugins.ml2 import config as config from neutron.plugins.ml2 import plugin from neutron.tests.unit.plugins.ml2 import test_plugin @@ -37,6 +34,8 @@ from neutron.tests.unit import testlib_api cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') +HOST = 'fake-host' +PLUGIN_NAME = 'neutron.plugins.ml2.plugin.Ml2Plugin' SECURITY_GROUP = '2f9244b4-9bee-4e81-bc4a-3f3c2045b3d7' SG_FAKE_ID = 'sg_fake_uuid' SG_RULE_FAKE_ID = 'sg_rule_fake_uuid' @@ -124,21 +123,14 @@ class DataMatcher(object): else: self._data = context.current.copy() self._object_type = object_type - filters.filter_for_odl(object_type, operation, self._data) + filter_cls = filters.FILTER_MAP[object_type] + attr_filter = getattr(filter_cls, 'filter_%s_attributes' % operation) + attr_filter(self._data) def __eq__(self, s): data = jsonutils.loads(s) return self._data == data[self._object_type] - def __ne__(self, s): - return not self.__eq__(s) - - -class AttributeDict(dict): - def __init__(self, *args, **kwargs): - super(AttributeDict, self).__init__(*args, **kwargs) - self.__dict__ = self - class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): def setUp(self): @@ -209,19 +201,15 @@ class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): 'binding:vnic_type': 'normal', 'binding:vif_type': 'unbound', 'mac_address': '12:34:56:78:21:b6'} - _network = OpenDaylightMechanismDriverTestCase.\ - _get_mock_network_operation_context().current - _plugin = manager.NeutronManager.get_plugin() - _plugin.get_security_group = mock.Mock(return_value=SECURITY_GROUP) - _plugin.get_port = mock.Mock(return_value=current) - _plugin.get_network = mock.Mock(return_value=_network) - _plugin_context_mock = {'session': neutron_db_api.get_session()} - _network_context_mock = {'_network': _network} - context = {'current': AttributeDict(current), - '_plugin': _plugin, - '_plugin_context': AttributeDict(_plugin_context_mock), - '_network_context': AttributeDict(_network_context_mock)} - return AttributeDict(context) + context = mock.Mock(current=current) + context._plugin.get_security_group = mock.Mock( + return_value=SECURITY_GROUP) + context._plugin.get_port = mock.Mock(return_value=current) + context._plugin_context.session = neutron_db_api.get_session() + context._network_context = mock.Mock( + _network=OpenDaylightMechanismDriverTestCase. + _get_mock_network_operation_context().current) + return context @staticmethod def _get_mock_security_group_operation_context(): @@ -285,9 +273,7 @@ class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): context = self._get_mock_operation_context(object_type) if object_type in [odl_const.ODL_SG, odl_const.ODL_SG_RULE]: - res_type = [rt for rt in callback._RESOURCE_MAPPING.values() - if rt.singular == object_type][0] - self.mech.sync_from_callback(operation, res_type, + self.mech.sync_from_callback(operation, object_type + 's', context[object_type]['id'], context) else: method = getattr(self.mech, '%s_%s_precommit' % (operation, @@ -413,26 +399,6 @@ class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): self._test_object_operation_pending_another_object_operation( parent, odl_const.ODL_DELETE, child, odl_const.ODL_DELETE) - def _test_cleanup_processing_rows(self, last_retried, expected_state): - # Create a dummy network (creates db row in pending state). - self._call_operation_object(odl_const.ODL_CREATE, - odl_const.ODL_NETWORK) - - # Get pending row and mark as processing and update - # the last_retried time - row = db.get_all_db_rows_by_state(self.db_session, - odl_const.PENDING)[0] - row.last_retried = last_retried - db.update_db_row_state(self.db_session, row, odl_const.PROCESSING) - - # Test if the cleanup marks this in the desired state - # based on the last_retried timestamp - cleanup.JournalCleanup().cleanup_processing_rows(self.db_session) - - # Verify that the Db row is in the desired state - rows = db.get_all_db_rows_by_state(self.db_session, expected_state) - self.assertEqual(1, len(rows)) - def test_driver(self): for operation in [odl_const.ODL_CREATE, odl_const.ODL_UPDATE, odl_const.ODL_DELETE]: @@ -440,20 +406,6 @@ class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): odl_const.ODL_PORT]: self._test_operation_object(operation, object_type) - def test_port_precommit_no_tenant(self): - context = self._get_mock_operation_context(odl_const.ODL_PORT) - context.current['tenant_id'] = '' - - method = getattr(self.mech, 'create_port_precommit') - method(context) - - # Verify that the Db row has a tenant - rows = db.get_all_db_rows_by_state(self.db_session, odl_const.PENDING) - self.assertEqual(1, len(rows)) - _network = OpenDaylightMechanismDriverTestCase.\ - _get_mock_network_operation_context().current - self.assertEqual(_network['tenant_id'], rows[0]['data']['tenant_id']) - def test_network(self): self._test_object_type(odl_const.ODL_NETWORK) @@ -523,14 +475,6 @@ class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): def test_port_processing_network(self): self._test_object_type_processing_network(odl_const.ODL_PORT) - def test_cleanup_processing_rows_time_not_expired(self): - self._test_cleanup_processing_rows(datetime.datetime.utcnow(), - odl_const.PROCESSING) - - def test_cleanup_processing_rows_time_expired(self): - old_time = datetime.datetime.utcnow() - datetime.timedelta(hours=24) - self._test_cleanup_processing_rows(old_time, odl_const.PENDING) - def test_thread_call(self): """Verify that the sync thread method is called.""" @@ -549,7 +493,7 @@ class OpenDaylightMechanismDriverTestCase(OpenDaylightConfigBase): self._test_object_type(odl_const.ODL_SG_RULE) def _decrease_row_created_time(self, row): - row.created_at -= datetime.timedelta(hours=1) + row.created_at -= timedelta(hours=1) self.db_session.merge(row) self.db_session.flush() diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_networking_topology.py b/networking-odl/networking_odl/tests/unit/ml2/test_networking_topology.py index fb83a7b..d342dde 100644 --- a/networking-odl/networking_odl/tests/unit/ml2/test_networking_topology.py +++ b/networking-odl/networking_odl/tests/unit/ml2/test_networking_topology.py @@ -21,11 +21,11 @@ from oslo_log import log from oslo_serialization import jsonutils import requests +from neutron.common import constants as n_constants from neutron.extensions import portbindings from neutron.plugins.common import constants from neutron.plugins.ml2 import driver_api from neutron.plugins.ml2 import driver_context -from neutron_lib import constants as n_constants from networking_odl.common import cache from networking_odl.ml2 import mech_driver diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_ovsdb_topology.py b/networking-odl/networking_odl/tests/unit/ml2/test_ovsdb_topology.py index 228154d..5502e5a 100644 --- a/networking-odl/networking_odl/tests/unit/ml2/test_ovsdb_topology.py +++ b/networking-odl/networking_odl/tests/unit/ml2/test_ovsdb_topology.py @@ -19,11 +19,11 @@ import mock from oslo_log import log from oslo_serialization import jsonutils +from neutron.common import constants as n_constants from neutron.extensions import portbindings from neutron.plugins.common import constants from neutron.plugins.ml2 import driver_api from neutron.plugins.ml2 import driver_context -from neutron_lib import constants as n_constants from networking_odl.ml2 import ovsdb_topology from networking_odl.tests import base diff --git a/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py b/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py deleted file mode 100644 index d69150c..0000000 --- a/networking-odl/networking_odl/tests/unit/ml2/test_pseudo_agentdb_binding.py +++ /dev/null @@ -1,334 +0,0 @@ -# Copyright (c) 2016 OpenStack Foundation -# 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. - -from copy import deepcopy -import mock -from os import path as os_path -from string import Template - -from neutron.extensions import portbindings -from neutron.plugins.common import constants -from neutron.plugins.ml2 import config -from neutron.plugins.ml2 import driver_api as api -from neutron.plugins.ml2 import driver_context as ctx -from neutron_lib import constants as n_const - -from networking_odl.ml2 import pseudo_agentdb_binding -from networking_odl.tests import base - -AGENTDB_BINARY = 'neutron-odlagent-portbinding' -L2_TYPE = "ODL L2" - - -class TestPseudoAgentDBBindingController(base.DietTestCase): - """Test class for AgentDBPortBinding.""" - - # test data hostconfig and hostconfig-dbget - sample_odl_hconfigs = {"hostconfigs": {"hostconfig": [ - {"host-id": "devstack", - "host-type": "ODL L2", - "config": """{"supported_vnic_types": [ - {"vnic_type": "normal", "vif_type": "ovs", - "vif_details": {}}], - "allowed_network_types": [ - "local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}"""} - ]}} - - # Test data for string interpolation of substitutable identifers - # e.g. $PORT_ID identifier in the configurations JSON string below shall - # be substituted with portcontext.current['id'] eliminating the check - # for specific vif_type making port-binding truly switch agnostic. - # Refer: Python string templates and interpolation (string.Template) - sample_hconf_str_tmpl_subs_vpp = { - "host": "devstack", # host-id in ODL JSON - "agent_type": "ODL L2", # host-type in ODL JSON - # config in ODL JSON - "configurations": """{"supported_vnic_types": [ - {"vnic_type": "normal", "vif_type": "vhostuser", - "vif_details": { - "uuid": "TEST_UUID", - "has_datapath_type_netdev": true, - "support_vhost_user": true, - "port_prefix": "socket_", - "vhostuser_socket_dir": "/tmp", - "vhostuser_ovs_plug": true, - "vhostuser_mode": "server", - "vhostuser_socket": - "/tmp/socket_$PORT_ID" - }}], - "allowed_network_types": [ - "local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}""" - } - - sample_hconf_str_tmpl_subs_ovs = { - "host": "devstack", # host-id in ODL JSON - "agent_type": "ODL L2", # host-type in ODL JSON - # config in ODL JSON - "configurations": """{"supported_vnic_types": [ - {"vnic_type": "normal", "vif_type": "vhostuser", - "vif_details": { - "uuid": "TEST_UUID", - "has_datapath_type_netdev": true, - "support_vhost_user": true, - "port_prefix": "vhu_", - "vhostuser_socket_dir": "/var/run/openvswitch", - "vhostuser_ovs_plug": true, - "vhostuser_mode": "client", - "vhostuser_socket": - "/var/run/openvswitch/vhu_$PORT_ID" - }}], - "allowed_network_types": [ - "local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}""" - } - - sample_hconf_str_tmpl_nosubs = { - "host": "devstack", # host-id in ODL JSON - "agent_type": "ODL L2", # host-type in ODL JSON - # config in ODL JSON - "configurations": """{"supported_vnic_types": [ - {"vnic_type": "normal", "vif_type": "ovs", - "vif_details": { - "uuid": "TEST_UUID", - "has_datapath_type_netdev": true, - "support_vhost_user": true, - "port_prefix": "socket_", - "vhostuser_socket_dir": "/tmp", - "vhostuser_ovs_plug": true, - "vhostuser_mode": "server", - "vhostuser_socket": - "/var/run/openvswitch/PORT_NOSUBS" - }}], - "allowed_network_types": [ - "local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}""" - } - - # Test data for vanilla OVS - sample_hconfig_dbget_ovs = {"configurations": {"supported_vnic_types": [ - {"vnic_type": "normal", "vif_type": portbindings.VIF_TYPE_OVS, - "vif_details": { - "some_test_details": None - }}], - "allowed_network_types": ["local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}} - - # Test data for OVS-DPDK - sample_hconfig_dbget_ovs_dpdk = {"configurations": { - "supported_vnic_types": [{ - "vnic_type": "normal", - "vif_type": portbindings.VIF_TYPE_VHOST_USER, - "vif_details": { - "uuid": "TEST_UUID", - "has_datapath_type_netdev": True, - "support_vhost_user": True, - "port_prefix": "vhu_", - # Assumption: /var/run mounted as tmpfs - "vhostuser_socket_dir": "/var/run/openvswitch", - "vhostuser_ovs_plug": True, - "vhostuser_mode": "client", - "vhostuser_socket": "/var/run/openvswitch/vhu_$PORT_ID"}}], - "allowed_network_types": ["local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}} - - # Test data for VPP - sample_hconfig_dbget_vpp = {"configurations": {"supported_vnic_types": [ - {"vnic_type": "normal", "vif_type": portbindings.VIF_TYPE_VHOST_USER, - "vif_details": { - "uuid": "TEST_UUID", - "has_datapath_type_netdev": True, - "support_vhost_user": True, - "port_prefix": "socket_", - "vhostuser_socket_dir": "/tmp", - "vhostuser_ovs_plug": True, - "vhostuser_mode": "server", - "vhostuser_socket": "/tmp/socket_$PORT_ID" - }}], - "allowed_network_types": ["local", "vlan", "vxlan", "gre"], - "bridge_mappings": {"physnet1": "br-ex"}}} - - # test data valid and invalid segments - test_valid_segment = { - api.ID: 'API_ID', - api.NETWORK_TYPE: constants.TYPE_LOCAL, - api.SEGMENTATION_ID: 'API_SEGMENTATION_ID', - api.PHYSICAL_NETWORK: 'API_PHYSICAL_NETWORK'} - - test_invalid_segment = { - api.ID: 'API_ID', - api.NETWORK_TYPE: constants.TYPE_NONE, - api.SEGMENTATION_ID: 'API_SEGMENTATION_ID', - api.PHYSICAL_NETWORK: 'API_PHYSICAL_NETWORK'} - - def setUp(self): - """Setup test.""" - super(TestPseudoAgentDBBindingController, self).setUp() - - config.cfg.CONF.set_override('url', - 'http://localhost:8080' - '/controller/nb/v2/neutron', 'ml2_odl') - - fake_agents_db = mock.MagicMock() - fake_agents_db.create_or_update_agent = mock.MagicMock() - - self.mgr = pseudo_agentdb_binding.PseudoAgentDBBindingController( - db_plugin=fake_agents_db) - - def test_make_hostconf_uri(self): - """test make uri.""" - test_path = '/restconf/neutron:neutron/hostconfigs' - expected = "http://localhost:8080/restconf/neutron:neutron/hostconfigs" - test_uri = self.mgr._make_hostconf_uri(path=test_path) - - self.assertEqual(expected, test_uri) - - def test_update_agents_db(self): - """test agent update.""" - self.mgr._update_agents_db( - hostconfigs=self.sample_odl_hconfigs['hostconfigs']['hostconfig']) - self.mgr.agents_db.create_or_update_agent.assert_called_once() - - def test_is_valid_segment(self): - """Validate the _check_segment method.""" - all_network_types = [constants.TYPE_FLAT, constants.TYPE_GRE, - constants.TYPE_LOCAL, constants.TYPE_VXLAN, - constants.TYPE_VLAN, constants.TYPE_NONE] - - valid_types = { - network_type - for network_type in all_network_types - if self.mgr._is_valid_segment({api.NETWORK_TYPE: network_type}, { - 'allowed_network_types': [ - constants.TYPE_LOCAL, constants.TYPE_GRE, - constants.TYPE_VXLAN, constants.TYPE_VLAN]})} - - self.assertEqual({ - constants.TYPE_LOCAL, constants.TYPE_GRE, constants.TYPE_VXLAN, - constants.TYPE_VLAN}, valid_types) - - def test_bind_port_with_vif_type_ovs(self): - """test bind_port with vanilla ovs.""" - port_context = self._fake_port_context( - fake_segments=[self.test_invalid_segment, self.test_valid_segment]) - - vif_type = portbindings.VIF_TYPE_OVS - vif_details = {'some_test_details': None} - - self.mgr._hconfig_bind_port( - port_context, self.sample_hconfig_dbget_ovs) - - port_context.set_binding.assert_called_once_with( - self.test_valid_segment[api.ID], vif_type, - vif_details, status=n_const.PORT_STATUS_ACTIVE) - - def _set_pass_vif_details(self, port_context, vif_details): - """extract vif_details and update vif_details if needed.""" - vhostuser_socket_dir = vif_details.get( - 'vhostuser_socket_dir', '/var/run/openvswitch') - port_spec = vif_details.get( - 'port_prefix', 'vhu_') + port_context.current['id'] - socket_path = os_path.join(vhostuser_socket_dir, port_spec) - vif_details.update({portbindings.VHOST_USER_SOCKET: socket_path}) - - return vif_details - - def test_bind_port_with_vif_type_vhost_user(self): - """test bind_port with ovs-dpdk.""" - port_context = self._fake_port_context( - fake_segments=[self.test_invalid_segment, self.test_valid_segment], - host_agents=[deepcopy(self.sample_hconf_str_tmpl_subs_ovs)]) - - self.mgr.bind_port(port_context) - - pass_vif_type = portbindings.VIF_TYPE_VHOST_USER - pass_vif_details = self.sample_hconfig_dbget_ovs_dpdk[ - 'configurations']['supported_vnic_types'][0]['vif_details'] - self._set_pass_vif_details(port_context, pass_vif_details) - - port_context.set_binding.assert_called_once_with( - self.test_valid_segment[api.ID], pass_vif_type, - pass_vif_details, status=n_const.PORT_STATUS_ACTIVE) - - def test_bind_port_with_vif_type_vhost_user_vpp(self): - """test bind_port with vpp.""" - port_context = self._fake_port_context( - fake_segments=[self.test_invalid_segment, self.test_valid_segment], - host_agents=[deepcopy(self.sample_hconf_str_tmpl_subs_vpp)]) - - self.mgr.bind_port(port_context) - - pass_vif_type = portbindings.VIF_TYPE_VHOST_USER - pass_vif_details = self.sample_hconfig_dbget_vpp['configurations'][ - 'supported_vnic_types'][0]['vif_details'] - self._set_pass_vif_details(port_context, pass_vif_details) - - port_context.set_binding.assert_called_once_with( - self.test_valid_segment[api.ID], pass_vif_type, - pass_vif_details, status=n_const.PORT_STATUS_ACTIVE) - - def test_bind_port_without_valid_segment(self): - """test bind_port without a valid segment.""" - port_context = self._fake_port_context( - fake_segments=[self.test_invalid_segment]) - - self.mgr._hconfig_bind_port( - port_context, self.sample_hconfig_dbget_ovs) - - port_context.set_binding.assert_not_called() - - def test_no_str_template_substitution_in_configuration_string(self): - """Test for no identifier substituion in config JSON string.""" - port_context = self._fake_port_context( - fake_segments=[self.test_invalid_segment, self.test_valid_segment]) - - hconf_dict = self.mgr._substitute_hconfig_tmpl( - port_context, self.sample_hconf_str_tmpl_nosubs) - - test_string = hconf_dict['configurations'][ - 'supported_vnic_types'][0][ - 'vif_details'][portbindings.VHOST_USER_SOCKET] - - expected_str = '/var/run/openvswitch/PORT_NOSUBS' - - self.assertEqual(expected_str, test_string) - - def test_str_template_substitution_in_configuration_string(self): - """Test for identifier substitution in config JSON string.""" - port_context = self._fake_port_context( - fake_segments=[self.test_invalid_segment, self.test_valid_segment]) - - hconf_dict = self.mgr._substitute_hconfig_tmpl( - port_context, self.sample_hconf_str_tmpl_subs_vpp) - - test_string = hconf_dict['configurations'][ - 'supported_vnic_types'][0][ - 'vif_details'][portbindings.VHOST_USER_SOCKET] - - expected_str = Template('/tmp/socket_$PORT_ID') - expected_str = expected_str.safe_substitute({ - 'PORT_ID': port_context.current['id']}) - - self.assertEqual(expected_str, test_string) - - def _fake_port_context(self, fake_segments, host_agents=None): - network = mock.MagicMock(spec=api.NetworkContext) - return mock.MagicMock( - spec=ctx.PortContext, - current={'id': 'CONTEXT_ID', - portbindings.VNIC_TYPE: portbindings.VNIC_NORMAL}, - segments_to_bind=fake_segments, network=network, - host_agents=lambda agent_type: host_agents) diff --git a/networking-odl/rally-jobs/plugins/README.rst b/networking-odl/rally-jobs/plugins/README.rst index 68ad548..33bec0d 100644 --- a/networking-odl/rally-jobs/plugins/README.rst +++ b/networking-odl/rally-jobs/plugins/README.rst @@ -1,7 +1,7 @@ Rally plugins ============= -All \*.py modules from this directory will be auto-loaded by Rally and all +All *.py modules from this directory will be auto-loaded by Rally and all plugins will be discoverable. There is no need of any extra configuration and there is no difference between writing them here and in rally code base. diff --git a/networking-odl/releasenotes/notes/.placeholder b/networking-odl/releasenotes/notes/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/networking-odl/releasenotes/notes/.placeholder +++ /dev/null diff --git a/networking-odl/releasenotes/source/_static/.placeholder b/networking-odl/releasenotes/source/_static/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/networking-odl/releasenotes/source/_static/.placeholder +++ /dev/null diff --git a/networking-odl/releasenotes/source/_templates/.placeholder b/networking-odl/releasenotes/source/_templates/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/networking-odl/releasenotes/source/_templates/.placeholder +++ /dev/null diff --git a/networking-odl/releasenotes/source/conf.py b/networking-odl/releasenotes/source/conf.py deleted file mode 100644 index 64166dc..0000000 --- a/networking-odl/releasenotes/source/conf.py +++ /dev/null @@ -1,264 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Networking OpenDaylight Release Notes documentation build configuration file, created by -# sphinx-quickstart on Fri Jul 22 14:54:21 2016. -# -# This file is execfile()d with the current directory set to its -# containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys -import os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) - -# -- General configuration ------------------------------------------------ - -# If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be -# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom -# ones. -extensions = [ - 'oslosphinx', - 'reno.sphinxext', -] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'Networking OpenDaylight Release Notes' -copyright = u'2016, networking-odl developers' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -import pbr.version -version_info = pbr.version.VersionInfo('networking-odl') -# The full version, including alpha/beta/rc tags. -release = version_info.canonical_version_string() -# The short X.Y version. -version = version_info.version_string_with_vcs() - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = [] - -# The reST default role (used for this markup: `text`) to use for all -# documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - -# If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False - - -# -- Options for HTML output ---------------------------------------------- - -# The theme to use for HTML and HTML Help pages. See the documentation for -# a list of builtin themes. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# "<project> v<release> documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] - -# Add any extra paths that contain custom files (such as robots.txt or -# .htaccess) here, relative to this directory. These files are copied -# directly to the root of the documentation. -#html_extra_path = [] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a <link> tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None - -# Output file base name for HTML help builder. -htmlhelp_basename = 'NetworkingOpenDaylightReleaseNotesdoc' - - -# -- Options for LaTeX output --------------------------------------------- - -latex_elements = { -# The paper size ('letterpaper' or 'a4paper'). -#'papersize': 'letterpaper', - -# The font size ('10pt', '11pt' or '12pt'). -#'pointsize': '10pt', - -# Additional stuff for the LaTeX preamble. -#'preamble': '', -} - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, -# author, documentclass [howto, manual, or own class]). -latex_documents = [ - ('index', 'NetworkingOpenDaylightReleaseNotes.tex', u'Networking OpenDaylight Release Notes Documentation', - u'networking-odl developers', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# If true, show page references after internal links. -#latex_show_pagerefs = False - -# If true, show URL addresses after external links. -#latex_show_urls = False - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output --------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'networkingopendaylightreleasenotes', u'Networking OpenDaylight Release Notes Documentation', - [u'networking-odl developers'], 1) -] - -# If true, show URL addresses after external links. -#man_show_urls = False - - -# -- Options for Texinfo output ------------------------------------------- - -# Grouping the document tree into Texinfo files. List of tuples -# (source start file, target name, title, author, -# dir menu entry, description, category) -texinfo_documents = [ - ('index', 'NetworkingOpenDaylightReleaseNotes', u'Networking OpenDaylight Release Notes Documentation', - u'networking-odl developers', 'NetworkingOpenDaylightReleaseNotes', 'One line description of project.', - 'Miscellaneous'), -] - -# Documents to append as an appendix to all manuals. -#texinfo_appendices = [] - -# If false, no module index is generated. -#texinfo_domain_indices = True - -# How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' - -# If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False diff --git a/networking-odl/releasenotes/source/index.rst b/networking-odl/releasenotes/source/index.rst deleted file mode 100644 index 3cd7a99..0000000 --- a/networking-odl/releasenotes/source/index.rst +++ /dev/null @@ -1,14 +0,0 @@ -.. Networking OpenDaylight Release Notes documentation master file, created by - sphinx-quickstart on Fri Jul 22 14:54:21 2016. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. - -Welcome to Networking OpenDaylight Release Notes's documentation! -================================================================= - -Contents: - -.. toctree:: - :maxdepth: 2 - - unreleased diff --git a/networking-odl/releasenotes/source/unreleased.rst b/networking-odl/releasenotes/source/unreleased.rst deleted file mode 100644 index 875030f..0000000 --- a/networking-odl/releasenotes/source/unreleased.rst +++ /dev/null @@ -1,5 +0,0 @@ -============================ -Current Series Release Notes -============================ - -.. release-notes:: diff --git a/networking-odl/requirements.txt b/networking-odl/requirements.txt index 7afa0be..9d881c4 100644 --- a/networking-odl/requirements.txt +++ b/networking-odl/requirements.txt @@ -3,6 +3,6 @@ # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 -Babel>=2.3.4 # BSD -stevedore>=1.16.0 # Apache-2.0 -neutron-lib>=0.3.0 # Apache-2.0 +Babel!=2.3.0,!=2.3.1,!=2.3.2,!=2.3.3,>=1.3 # BSD +stevedore>=1.5.0 # Apache-2.0 +neutron-lib>=0.0.1 # Apache-2.0 diff --git a/networking-odl/setup.cfg b/networking-odl/setup.cfg index 4121690..b2ef16e 100644 --- a/networking-odl/setup.cfg +++ b/networking-odl/setup.cfg @@ -15,9 +15,6 @@ classifier = Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 - Programming Language :: Python :: 3 - Programming Language :: Python :: 3.4 - Programming Language :: Python :: 3.5 [files] packages = @@ -52,8 +49,6 @@ setup-hooks = # mechanism_drivers = opendaylight_v2 # [DEFAULT] # service_plugins = odl-router_v2 -console_scripts = - neutron-odl-ovs-hostconfig = networking_odl.cmd.set_ovs_hostconfigs:main neutron.ml2.mechanism_drivers = opendaylight = networking_odl.ml2.mech_driver:OpenDaylightMechanismDriver opendaylight_v2 = networking_odl.ml2.mech_driver_v2:OpenDaylightMechanismDriver @@ -65,23 +60,12 @@ neutron.db.alembic_migrations = networking_odl.ml2.port_binding_controllers = network-topology = networking_odl.ml2.network_topology:NetworkTopologyManager legacy-port-binding = networking_odl.ml2.legacy_port_binding:LegacyPortBindingManager - pseudo-agentdb-binding = networking_odl.ml2.pseudo_agentdb_binding:PseudoAgentDBBindingController -oslo.config.opts = - ml2_odl = networking_odl.common.config:list_opts [build_sphinx] all_files = 1 build-dir = doc/build source-dir = doc/source -[upload_sphinx] -upload-dir = doc/build/html - -[build_releasenotes] -build-dir = releasenotes/build -source-dir = releasenotes/source -all_files = 1 - [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg diff --git a/networking-odl/test-requirements.txt b/networking-odl/test-requirements.txt index 7b35758..e13bb5e 100644 --- a/networking-odl/test-requirements.txt +++ b/networking-odl/test-requirements.txt @@ -5,16 +5,12 @@ hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 -doc8 # Apache-2.0 +discover # BSD python-subunit>=0.0.18 # Apache-2.0/BSD -sphinx!=1.3b1,<1.3,>=1.2.1 # BSD +sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD -testresources>=0.2.4 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD WebTest>=2.0 # MIT testtools>=1.4.0 # MIT - -# releasenotes -reno>=1.8.0 # Apache2 diff --git a/networking-odl/tox.ini b/networking-odl/tox.ini index 644dbf3..c31c53d 100644 --- a/networking-odl/tox.ini +++ b/networking-odl/tox.ini @@ -1,11 +1,10 @@ [tox] -envlist = docs,py35,py34,py27,pep8 +envlist = py27,py34,pep8 minversion = 1.6 skipsdist = True [testenv] setenv = VIRTUAL_ENV={envdir} - PYTHONWARNINGS=default::DeprecationWarning passenv = http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy NO_PROXY usedevelop = True install_command = pip install -r requirements.txt -U {opts} {packages} @@ -20,15 +19,12 @@ commands = bash tools/pretty_tox.sh '{posargs}' [testenv:pep8] commands = flake8 - doc8 doc/source devstack releasenotes/source rally-jobs neutron-db-manage --subproject networking-odl check_migration [testenv:i18n] commands = python ./tools/check_i18n.py ./networking_odl ./tools/i18n_cfg.py [testenv:venv] -# NOTE(yamahata): translation job can't use zuul-cloner or upper-constraints -install_command = pip install -U {opts} {packages} commands = {posargs} [testenv:cover] @@ -37,21 +33,12 @@ commands = coverage report [testenv:docs] -commands = - doc8 doc/source devstack releasenotes/source rally-jobs - python setup.py build_sphinx - -[testenv:debug] -commands = oslo_debug_helper -t networking_odl/tests {posargs} +commands = python setup.py build_sphinx [hacking] import_exceptions = networking_odl._i18n local-check-factory = neutron_lib.hacking.checks.factory -[doc8] -# File extensions to check -extensions = .rst - [flake8] # H803 skipped on purpose per list discussion. # E123, E125 skipped as they are invalid PEP-8. @@ -59,14 +46,4 @@ extensions = .rst # N530 direct neutron imports not allowed show-source = True ignore = E123,E125,H803,N530 -exclude=./.*,dist,doc,releasenotes,*lib/python*,*egg,build,tools - -[testenv:genconfig] -deps = -r{toxinidir}/requirements.txt -commands = - mkdir -p etc/neutron/plugins/ml2 - oslo-config-generator --namespace ml2_odl --output-file etc/neutron/plugins/ml2/ml2_conf_odl.ini.sample -whitelist_externals = mkdir - -[testenv:releasenotes] -commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html +exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools |