New APIC mechanism and extension drivers
This is a very preliminary version of a new APIC mechanism driver
utilizing the ACI Integration Module (AIM) library concurrently being
developed. A corresponding extension driver exposes details regarding
the mapping of the Neutron resources to APIC. These drivers require
the Ml2Plus extended driver APIs.
See the apic-aim-ml2-driver devref for implementation details and for
devstack configuration instructions.
Conflicts:
	devstack/settings
Change-Id: I82df32f0880d6a0d53b305f6c6391fcbea049d1b
(cherry picked from commit 6e307e0a38)
			
			
This commit is contained in:
		
							
								
								
									
										49
									
								
								devstack/lib/apic_aim
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								devstack/lib/apic_aim
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					function install_apic_aim {
 | 
				
			||||||
 | 
					    echo_summary "Installing apic_aim"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    install_apic_ml2
 | 
				
			||||||
 | 
					    install_aim
 | 
				
			||||||
 | 
					    install_opflex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function configure_apic_aim {
 | 
				
			||||||
 | 
					    echo_summary "Configuring apic_aim"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # devstack/lib/neutron_plugins/ml2 does not allow overriding
 | 
				
			||||||
 | 
					    # Q_PLUGIN_CLASS in override_defaults, so do it here instread
 | 
				
			||||||
 | 
					    iniset $NEUTRON_CONF DEFAULT core_plugin ml2plus
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth auth_plugin v3password
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth auth_url $KEYSTONE_SERVICE_URI_V3
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth username admin
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth password $ADMIN_PASSWORD
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth user_domain_name default
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth project_domain_name default
 | 
				
			||||||
 | 
					    iniset /$Q_PLUGIN_CONF_FILE apic_aim_auth project_name admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    init_aim
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function install_aim {
 | 
				
			||||||
 | 
					    git_clone $AIM_REPO $AIM_DIR $AIM_BRANCH
 | 
				
			||||||
 | 
					    mv $AIM_DIR/test-requirements.txt $AIM_DIR/_test-requirements.txt
 | 
				
			||||||
 | 
					    setup_develop $AIM_DIR
 | 
				
			||||||
 | 
					    mv $AIM_DIR/_test-requirements.txt $AIM_DIR/test-requirements.txt
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function init_aim {
 | 
				
			||||||
 | 
					    aim -c $NEUTRON_CONF db-migration upgrade
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function install_opflex {
 | 
				
			||||||
 | 
					    git_clone $OPFLEX_REPO $OPFLEX_DIR $OPFLEX_BRANCH
 | 
				
			||||||
 | 
					    mv $OPFLEX_DIR/test-requirements.txt $OPFLEX_DIR/_test-requirements.txt
 | 
				
			||||||
 | 
					    touch  $OPFLEX_DIR/setup.cfg
 | 
				
			||||||
 | 
					    setup_develop $OPFLEX_DIR
 | 
				
			||||||
 | 
					    mv $OPFLEX_DIR/_test-requirements.txt $OPFLEX_DIR/test-requirements.txt
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Tell emacs to use shell-script-mode
 | 
				
			||||||
 | 
					## Local variables:
 | 
				
			||||||
 | 
					## mode: shell-script
 | 
				
			||||||
 | 
					## End:
 | 
				
			||||||
@@ -26,6 +26,8 @@ AIM_REPO=http://github.com/noironetworks/aci-integration-module.git
 | 
				
			|||||||
AIM_DIR=$DEST/aim
 | 
					AIM_DIR=$DEST/aim
 | 
				
			||||||
APICML2_REPO=http://github.com/noironetworks/apic-ml2-driver.git
 | 
					APICML2_REPO=http://github.com/noironetworks/apic-ml2-driver.git
 | 
				
			||||||
APICML2_DIR=$DEST/apic_ml2
 | 
					APICML2_DIR=$DEST/apic_ml2
 | 
				
			||||||
 | 
					OPFLEX_REPO=http://github.com/noironetworks/python-opflex-agent.git
 | 
				
			||||||
 | 
					OPFLEX_DIR=$DEST/opflexagent
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Save trace setting
 | 
					# Save trace setting
 | 
				
			||||||
XTRACE=$(set +o | grep xtrace)
 | 
					XTRACE=$(set +o | grep xtrace)
 | 
				
			||||||
@@ -81,17 +83,6 @@ function install_gbpui {
 | 
				
			|||||||
    mv $GBPUI_DIR/_test-requirements.txt $GBPUI_DIR/test-requirements.txt
 | 
					    mv $GBPUI_DIR/_test-requirements.txt $GBPUI_DIR/test-requirements.txt
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function install_aim {
 | 
					 | 
				
			||||||
    git_clone $AIM_REPO $AIM_DIR $AIM_BRANCH
 | 
					 | 
				
			||||||
    mv $AIM_DIR/test-requirements.txt $AIM_DIR/_test-requirements.txt
 | 
					 | 
				
			||||||
    setup_develop $AIM_DIR
 | 
					 | 
				
			||||||
    mv $AIM_DIR/_test-requirements.txt $AIM_DIR/test-requirements.txt
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function init_aim {
 | 
					 | 
				
			||||||
    aim -c $NEUTRON_CONF db-migration upgrade
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function install_apic_ml2 {
 | 
					function install_apic_ml2 {
 | 
				
			||||||
    git_clone $APICML2_REPO $APICML2_DIR $APICML2_BRANCH
 | 
					    git_clone $APICML2_REPO $APICML2_DIR $APICML2_BRANCH
 | 
				
			||||||
    mv $APICML2_DIR/test-requirements.txt $APICML2_DIR/_test-requirements.txt
 | 
					    mv $APICML2_DIR/test-requirements.txt $APICML2_DIR/_test-requirements.txt
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1 +1,13 @@
 | 
				
			|||||||
NEUTRON_CREATE_INITIAL_NETWORKS="False"
 | 
					NEUTRON_CREATE_INITIAL_NETWORKS="False"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ENABLE_APIC_AIM=${ENABLE_APIC_AIM:-False}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if [[ $ENABLE_APIC_AIM = True ]]; then
 | 
				
			||||||
 | 
					    echo_summary "Overriding defaults for apic_aim"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Q_PLUGIN=${Q_PLUGIN:-ml2}
 | 
				
			||||||
 | 
					    Q_ML2_TENANT_NETWORK_TYPE=${Q_ML2_TENANT_NETWORK_TYPE:-opflex}
 | 
				
			||||||
 | 
					    Q_ML2_PLUGIN_TYPE_DRIVERS=${Q_ML2_PLUGIN_TYPE_DRIVERS:-local,vlan,opflex}
 | 
				
			||||||
 | 
					    Q_ML2_PLUGIN_MECHANISM_DRIVERS=${Q_ML2_PLUGIN_MECHANISM_DRIVERS:-apic_aim}
 | 
				
			||||||
 | 
					    Q_ML2_PLUGIN_EXT_DRIVERS=${Q_ML2_PLUGIN_EXT_DRIVERS-apic_aim,port_security}
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -49,6 +49,7 @@ if is_service_enabled group-policy; then
 | 
				
			|||||||
        echo_summary "Preparing $GBP"
 | 
					        echo_summary "Preparing $GBP"
 | 
				
			||||||
    elif [[ "$1" == "stack" && "$2" == "install" ]]; then
 | 
					    elif [[ "$1" == "stack" && "$2" == "install" ]]; then
 | 
				
			||||||
        echo_summary "Installing $GBP"
 | 
					        echo_summary "Installing $GBP"
 | 
				
			||||||
 | 
					        [[ $ENABLE_APIC_AIM = True ]] && install_apic_aim
 | 
				
			||||||
        if [[ $ENABLE_NFP = True ]]; then
 | 
					        if [[ $ENABLE_NFP = True ]]; then
 | 
				
			||||||
            echo_summary "Installing $NFP"
 | 
					            echo_summary "Installing $NFP"
 | 
				
			||||||
            [[ $DISABLE_BUILD_IMAGE = False ]] && prepare_nfp_image_builder
 | 
					            [[ $DISABLE_BUILD_IMAGE = False ]] && prepare_nfp_image_builder
 | 
				
			||||||
@@ -60,9 +61,8 @@ if is_service_enabled group-policy; then
 | 
				
			|||||||
        gbp_configure_neutron
 | 
					        gbp_configure_neutron
 | 
				
			||||||
        [[ $ENABLE_NFP = True ]] && echo_summary "Configuring $NFP"
 | 
					        [[ $ENABLE_NFP = True ]] && echo_summary "Configuring $NFP"
 | 
				
			||||||
        [[ $ENABLE_NFP = True ]] && nfp_configure_neutron
 | 
					        [[ $ENABLE_NFP = True ]] && nfp_configure_neutron
 | 
				
			||||||
#        install_apic_ml2
 | 
					        # REVISIT move installs to install phase?
 | 
				
			||||||
#        install_aim
 | 
					        # install_apic_ml2
 | 
				
			||||||
#        init_aim
 | 
					 | 
				
			||||||
        install_gbpclient
 | 
					        install_gbpclient
 | 
				
			||||||
        install_gbpservice
 | 
					        install_gbpservice
 | 
				
			||||||
        [[ $ENABLE_NFP = True ]] && install_nfpgbpservice
 | 
					        [[ $ENABLE_NFP = True ]] && install_nfpgbpservice
 | 
				
			||||||
@@ -70,6 +70,7 @@ if is_service_enabled group-policy; then
 | 
				
			|||||||
        [[ $ENABLE_NFP = True ]] && init_nfpgbpservice
 | 
					        [[ $ENABLE_NFP = True ]] && init_nfpgbpservice
 | 
				
			||||||
        install_gbpheat
 | 
					        install_gbpheat
 | 
				
			||||||
        install_gbpui
 | 
					        install_gbpui
 | 
				
			||||||
 | 
					        [[ $ENABLE_APIC_AIM = True ]] && configure_apic_aim
 | 
				
			||||||
        stop_apache_server
 | 
					        stop_apache_server
 | 
				
			||||||
        start_apache_server
 | 
					        start_apache_server
 | 
				
			||||||
    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
 | 
					    elif [[ "$1" == "stack" && "$2" == "extra" ]]; then
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,8 @@
 | 
				
			|||||||
# Make sure the plugin name in local.conf is "gbp", as in: enable_plugin gbp <remote> <branch>
 | 
					# Make sure the plugin name in local.conf is "gbp", as in: enable_plugin gbp <remote> <branch>
 | 
				
			||||||
source $DEST/gbp/devstack/lib/gbp
 | 
					source $DEST/gbp/devstack/lib/gbp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					[[ $ENABLE_APIC_AIM = True ]] && source $DEST/gbp/devstack/lib/apic_aim
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ENABLE_NFP=${ENABLE_NFP:-False}
 | 
					ENABLE_NFP=${ENABLE_NFP:-False}
 | 
				
			||||||
[[ $ENABLE_NFP = True ]] && source $DEST/gbp/devstack/lib/nfp
 | 
					[[ $ENABLE_NFP = True ]] && source $DEST/gbp/devstack/lib/nfp
 | 
				
			||||||
[[ $ENABLE_NFP = True ]] && DISABLE_BUILD_IMAGE=${DISABLE_BUILD_IMAGE:-False}
 | 
					[[ $ENABLE_NFP = True ]] && DISABLE_BUILD_IMAGE=${DISABLE_BUILD_IMAGE:-False}
 | 
				
			||||||
@@ -21,14 +24,20 @@ GBPHEAT_REPO=${GBPHEAT_REPO:-${GIT_BASE}/openstack/group-based-policy-automation
 | 
				
			|||||||
GBPHEAT_BRANCH=${GBPHEAT_BRANCH:-stable/mitaka}
 | 
					GBPHEAT_BRANCH=${GBPHEAT_BRANCH:-stable/mitaka}
 | 
				
			||||||
AIM_BRANCH=${AIM_BRANCH:-master}
 | 
					AIM_BRANCH=${AIM_BRANCH:-master}
 | 
				
			||||||
APICML2_BRANCH=${APICML2_BRANCH:-stable/mitaka}
 | 
					APICML2_BRANCH=${APICML2_BRANCH:-stable/mitaka}
 | 
				
			||||||
 | 
					OPFLEX_BRANCH=${OPFLEX_BRANCH:-master}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Enable necessary services, including group-policy (and disable others)
 | 
					# Enable necessary services, including group-policy (and disable others)
 | 
				
			||||||
disable_service n-net
 | 
					disable_service n-net
 | 
				
			||||||
enable_service n-novnc
 | 
					enable_service n-novnc
 | 
				
			||||||
enable_service q-svc
 | 
					enable_service q-svc
 | 
				
			||||||
enable_service q-agt
 | 
					if [[ $ENABLE_APIC_AIM = True ]]; then
 | 
				
			||||||
 | 
					   disable_service q-agt
 | 
				
			||||||
 | 
					   disable_service q-l3
 | 
				
			||||||
 | 
					else
 | 
				
			||||||
 | 
					   enable_service q-agt
 | 
				
			||||||
 | 
					   enable_service q-l3
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
enable_service q-dhcp
 | 
					enable_service q-dhcp
 | 
				
			||||||
enable_service q-l3
 | 
					 | 
				
			||||||
enable_service q-fwaas
 | 
					enable_service q-fwaas
 | 
				
			||||||
enable_service q-lbaas
 | 
					enable_service q-lbaas
 | 
				
			||||||
enable_service q-meta
 | 
					enable_service q-meta
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										91
									
								
								doc/source/devref/apic-aim-ml2-driver.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								doc/source/devref/apic-aim-ml2-driver.rst
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					..
 | 
				
			||||||
 | 
					 This work is licensed under a Creative Commons Attribution 3.0 Unported
 | 
				
			||||||
 | 
					 License.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 http://creativecommons.org/licenses/by/3.0/legalcode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APIC-AIM ML2 Driver
 | 
				
			||||||
 | 
					===================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The APIC-AIM ML2 mechanism driver and associated extension driver
 | 
				
			||||||
 | 
					utilize the ACI Integration Module (AIM) library to provide improved
 | 
				
			||||||
 | 
					integration between Neutron and the Cisco APIC. The most significant
 | 
				
			||||||
 | 
					advantage of the APIC-AIM ML2 drivers over the previous APIC ML2
 | 
				
			||||||
 | 
					drivers is that they are intended to coexist with the AIM GBP policy
 | 
				
			||||||
 | 
					driver, providing full simultaneous support for both Neutron and GBP
 | 
				
			||||||
 | 
					APIs within the same OpenStack deployment, including sharing of
 | 
				
			||||||
 | 
					resources between Neutron and GBP workflows where appropriate.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Additionally, the AIM-based mechanism and policy driver architecture
 | 
				
			||||||
 | 
					is completely transactional, and thus provides improved robustness,
 | 
				
			||||||
 | 
					performance, and scalability. A set of replicated AIM daemons is
 | 
				
			||||||
 | 
					responsible for continually maintaining consistency between the AIM DB
 | 
				
			||||||
 | 
					state specified by the drivers and the APIC state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ML2Plus Plugin
 | 
				
			||||||
 | 
					--------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The ML2Plus core plugin extends the ML2 plugin with several driver API
 | 
				
			||||||
 | 
					features that are needed for APIC AIM support. An extended
 | 
				
			||||||
 | 
					MechanismDriver abstract base class adds an ensure_tenant() method
 | 
				
			||||||
 | 
					that is called before any transaction creating a new resource, and
 | 
				
			||||||
 | 
					(soon) adds precommit and postcommit calls for operations on
 | 
				
			||||||
 | 
					additional resources such as address scope. An extended
 | 
				
			||||||
 | 
					ExtensionDriver base class will support extending those additional
 | 
				
			||||||
 | 
					resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ML2 configuration is unchanged, and compatibility is maintained with
 | 
				
			||||||
 | 
					all existing ML2 drivers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APIC-AIM Mechanism Driver
 | 
				
			||||||
 | 
					-------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The apic-aim ML2 mechanism driver maps Neutron resources to the APIC
 | 
				
			||||||
 | 
					resource configurations that provide the required Neutron networking
 | 
				
			||||||
 | 
					semantics. Currently, the following Neutron -> AIM mappings are
 | 
				
			||||||
 | 
					implemented:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 tenant -> Tenant, ApplicationProfile
 | 
				
			||||||
 | 
					 network -> BridgeDomain, default EndpointGroup
 | 
				
			||||||
 | 
					 subnet -> Subnet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Neutron ports are realized as Endpoints within an APIC
 | 
				
			||||||
 | 
					EndpointGroup. A port created using Neutron APIs belongs to the
 | 
				
			||||||
 | 
					network's default EndpointGroup. A port created as a GBP PolicyTarget
 | 
				
			||||||
 | 
					does not use its PolicyTargetGroup's L2Policy's network's default
 | 
				
			||||||
 | 
					EndpointGroup, but instead belongs to an APIC EndpointGroup mapped
 | 
				
			||||||
 | 
					from its PolicyTargetGroup.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Additional mappings that are under development include:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 address scope -> VRF
 | 
				
			||||||
 | 
					 router -> contract rules
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Port binding for the OpFlex L2 agent and support for the
 | 
				
			||||||
 | 
					get_gbp_details RPC are implemented. DVS port binding and other RPCs
 | 
				
			||||||
 | 
					remain to be implemented.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APIC-AIM Extension Driver
 | 
				
			||||||
 | 
					-------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The apic-aim ML2 extension driver provides administrators with read
 | 
				
			||||||
 | 
					access to the distinguished names of the set of APIC resources to
 | 
				
			||||||
 | 
					which each Neutron resource is mapped, as well as to APIC-specific
 | 
				
			||||||
 | 
					status and AIM daemon synchronization status for those resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The extension driver may eventually also allow DNs of existing APIC
 | 
				
			||||||
 | 
					resources to be specified when creating Neutron resources.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DevStack Support
 | 
				
			||||||
 | 
					----------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The ML2Plus core plugin and APIC-AIM mechanism and extension drivers
 | 
				
			||||||
 | 
					can be configured by including the following in local.conf when
 | 
				
			||||||
 | 
					running devstack::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enable_plugin gbp https://git.openstack.org/openstack/group-based-policy master
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ENABLE_APIC_AIM=True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Note that the GBP devstack plugin installs the python-opflex-agent
 | 
				
			||||||
 | 
					repo, but does not yet configure or run the OpFlex L2 agent.
 | 
				
			||||||
@@ -46,6 +46,7 @@ GBP Design
 | 
				
			|||||||
   shared-resources
 | 
					   shared-resources
 | 
				
			||||||
   traffic-stitching-plumber-model
 | 
					   traffic-stitching-plumber-model
 | 
				
			||||||
   traffic-stitching-plumber-placement-type
 | 
					   traffic-stitching-plumber-placement-type
 | 
				
			||||||
 | 
					   apic-aim-ml2-driver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Module Reference
 | 
					Module Reference
 | 
				
			||||||
----------------
 | 
					----------------
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,6 +41,8 @@
 | 
				
			|||||||
    "get_network:provider:physical_network": "rule:admin_only",
 | 
					    "get_network:provider:physical_network": "rule:admin_only",
 | 
				
			||||||
    "get_network:provider:segmentation_id": "rule:admin_only",
 | 
					    "get_network:provider:segmentation_id": "rule:admin_only",
 | 
				
			||||||
    "get_network:queue_id": "rule:admin_only",
 | 
					    "get_network:queue_id": "rule:admin_only",
 | 
				
			||||||
 | 
					    "get_network:apic:distinguished_names": "rule:admin_only",
 | 
				
			||||||
 | 
					    "get_network:apic:synchronization_state": "rule:admin_only",
 | 
				
			||||||
    "create_network:shared": "rule:admin_only",
 | 
					    "create_network:shared": "rule:admin_only",
 | 
				
			||||||
    "create_network:router:external": "rule:admin_only",
 | 
					    "create_network:router:external": "rule:admin_only",
 | 
				
			||||||
    "create_network:segments": "rule:admin_only",
 | 
					    "create_network:segments": "rule:admin_only",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,133 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 re
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from neutron._i18n import _LI
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					NAME_TYPE_TENANT = 'tenant'
 | 
				
			||||||
 | 
					NAME_TYPE_NETWORK = 'network'
 | 
				
			||||||
 | 
					NAME_TYPE_POLICY_TARGET_GROUP = 'policy_target_group'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MAX_APIC_NAME_LENGTH = 46
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# TODO(rkukura): This is name mapper is copied from the apicapi repo,
 | 
				
			||||||
 | 
					# and modified to pass in resource names rather than calling the core
 | 
				
			||||||
 | 
					# plugin to get them, and to use the existing DB session. We need
 | 
				
			||||||
 | 
					# decide whether to make these changes in apicapi (maybe on a branch),
 | 
				
			||||||
 | 
					# move this some other repo, or keep it here. The changes are not
 | 
				
			||||||
 | 
					# backwards compatible. The implementation should also be cleaned up
 | 
				
			||||||
 | 
					# and simplified. For example, sessions should be passed in place of
 | 
				
			||||||
 | 
					# contexts, and the core plugin calls eliminated.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def truncate(string, max_length):
 | 
				
			||||||
 | 
					    if max_length < 0:
 | 
				
			||||||
 | 
					        return ''
 | 
				
			||||||
 | 
					    return string[:max_length] if len(string) > max_length else string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class APICNameMapper(object):
 | 
				
			||||||
 | 
					    def __init__(self, db, log):
 | 
				
			||||||
 | 
					        self.db = db
 | 
				
			||||||
 | 
					        self.min_suffix = 5
 | 
				
			||||||
 | 
					        global LOG
 | 
				
			||||||
 | 
					        LOG = log.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def mapper(name_type):
 | 
				
			||||||
 | 
					        """Wrapper to land all the common operations between mappers."""
 | 
				
			||||||
 | 
					        def wrap(func):
 | 
				
			||||||
 | 
					            def inner(inst, session, resource_id, resource_name=None):
 | 
				
			||||||
 | 
					                saved_name = inst.db.get_apic_name(session,
 | 
				
			||||||
 | 
					                                                   resource_id,
 | 
				
			||||||
 | 
					                                                   name_type)
 | 
				
			||||||
 | 
					                if saved_name:
 | 
				
			||||||
 | 
					                    result = saved_name[0]
 | 
				
			||||||
 | 
					                    return result
 | 
				
			||||||
 | 
					                name = ''
 | 
				
			||||||
 | 
					                try:
 | 
				
			||||||
 | 
					                    name = func(inst, session, resource_id, resource_name)
 | 
				
			||||||
 | 
					                except Exception as e:
 | 
				
			||||||
 | 
					                    LOG.warn(("Exception in looking up name %s"), name_type)
 | 
				
			||||||
 | 
					                    LOG.error(e.message)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                purged_id = re.sub(r"-+", "-", resource_id)
 | 
				
			||||||
 | 
					                result = purged_id[:inst.min_suffix]
 | 
				
			||||||
 | 
					                if name:
 | 
				
			||||||
 | 
					                    name = re.sub(r"-+", "-", name)
 | 
				
			||||||
 | 
					                    # Keep as many uuid chars as possible
 | 
				
			||||||
 | 
					                    id_suffix = "_" + result
 | 
				
			||||||
 | 
					                    max_name_length = MAX_APIC_NAME_LENGTH - len(id_suffix)
 | 
				
			||||||
 | 
					                    result = truncate(name, max_name_length) + id_suffix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    result = truncate(result, MAX_APIC_NAME_LENGTH)
 | 
				
			||||||
 | 
					                    # Remove forbidden whitespaces
 | 
				
			||||||
 | 
					                    result = result.replace(' ', '')
 | 
				
			||||||
 | 
					                    result = inst._grow_id_if_needed(
 | 
				
			||||||
 | 
					                        session, purged_id, name_type, result,
 | 
				
			||||||
 | 
					                        start=inst.min_suffix)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    result = purged_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                inst.db.add_apic_name(session, resource_id,
 | 
				
			||||||
 | 
					                                      name_type, result)
 | 
				
			||||||
 | 
					                return result
 | 
				
			||||||
 | 
					            return inner
 | 
				
			||||||
 | 
					        return wrap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _grow_id_if_needed(self, session, resource_id, name_type,
 | 
				
			||||||
 | 
					                           current_result, start=0):
 | 
				
			||||||
 | 
					        result = current_result
 | 
				
			||||||
 | 
					        if result.endswith('_'):
 | 
				
			||||||
 | 
					            result = result[:-1]
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            x = 0
 | 
				
			||||||
 | 
					            while True:
 | 
				
			||||||
 | 
					                if self.db.get_filtered_apic_names(session,
 | 
				
			||||||
 | 
					                                                   neutron_type=name_type,
 | 
				
			||||||
 | 
					                                                   apic_name=result):
 | 
				
			||||||
 | 
					                    if x == 0 and start == 0:
 | 
				
			||||||
 | 
					                        result += '_'
 | 
				
			||||||
 | 
					                    # This name overlaps, add more ID characters
 | 
				
			||||||
 | 
					                    result += resource_id[start + x]
 | 
				
			||||||
 | 
					                    x += 1
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    break
 | 
				
			||||||
 | 
					        except AttributeError:
 | 
				
			||||||
 | 
					            LOG.info(_LI("Current DB API doesn't support "
 | 
				
			||||||
 | 
					                         "get_filtered_apic_names."))
 | 
				
			||||||
 | 
					        except IndexError:
 | 
				
			||||||
 | 
					            LOG.debug("Ran out of ID characters.")
 | 
				
			||||||
 | 
					        return result
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mapper(NAME_TYPE_TENANT)
 | 
				
			||||||
 | 
					    def tenant(self, session, tenant_id, tenant_name=None):
 | 
				
			||||||
 | 
					        return tenant_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mapper(NAME_TYPE_NETWORK)
 | 
				
			||||||
 | 
					    def network(self, session, network_id, network_name=None):
 | 
				
			||||||
 | 
					        return network_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @mapper(NAME_TYPE_POLICY_TARGET_GROUP)
 | 
				
			||||||
 | 
					    def policy_target_group(self, session, policy_target_group_id,
 | 
				
			||||||
 | 
					                            policy_target_group_name=None):
 | 
				
			||||||
 | 
					        return policy_target_group_name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_apic_name(self, session, object_id):
 | 
				
			||||||
 | 
					        self.db.delete_apic_name(session, object_id)
 | 
				
			||||||
							
								
								
									
										82
									
								
								gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/cache.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/cache.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 keystoneclient import auth as ksc_auth
 | 
				
			||||||
 | 
					from keystoneclient import session as ksc_session
 | 
				
			||||||
 | 
					from keystoneclient.v3 import client as ksc_client
 | 
				
			||||||
 | 
					from neutron._i18n import _LW
 | 
				
			||||||
 | 
					from oslo_config import cfg
 | 
				
			||||||
 | 
					from oslo_log import log as logging
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = logging.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# REVISIT(rkukura): We use keystone to get the name of the keystone
 | 
				
			||||||
 | 
					# project owning each neutron resource, which by default, requires
 | 
				
			||||||
 | 
					# admin. If we keep this, we should probably move it to a separate
 | 
				
			||||||
 | 
					# config module. But we should also investigate whether admin is even
 | 
				
			||||||
 | 
					# needed, or if neutron's credentials could somehow be used.
 | 
				
			||||||
 | 
					AUTH_GROUP = 'apic_aim_auth'
 | 
				
			||||||
 | 
					ksc_session.Session.register_conf_options(cfg.CONF, AUTH_GROUP)
 | 
				
			||||||
 | 
					ksc_auth.register_conf_options(cfg.CONF, AUTH_GROUP)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ProjectNameCache(object):
 | 
				
			||||||
 | 
					    """Cache of Keystone project ID to project name mappings."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        self.project_names = {}
 | 
				
			||||||
 | 
					        self.keystone = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def ensure_project(self, project_id):
 | 
				
			||||||
 | 
					        """Ensure cache contains mapping for project.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param project_id: ID of the project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Ensure that the cache contains a mapping for the project
 | 
				
			||||||
 | 
					        identified by project_id. If it is not, Keystone will be
 | 
				
			||||||
 | 
					        queried for the current list of projects, and any new mappings
 | 
				
			||||||
 | 
					        will be added to the cache. This method should never be called
 | 
				
			||||||
 | 
					        inside a transaction with a project_id not already in the
 | 
				
			||||||
 | 
					        cache.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if project_id not in self.project_names:
 | 
				
			||||||
 | 
					            if self.keystone is None:
 | 
				
			||||||
 | 
					                LOG.debug("Getting keystone client")
 | 
				
			||||||
 | 
					                auth = ksc_auth.load_from_conf_options(cfg.CONF, AUTH_GROUP)
 | 
				
			||||||
 | 
					                LOG.debug("Got auth: %s" % auth)
 | 
				
			||||||
 | 
					                if not auth:
 | 
				
			||||||
 | 
					                    LOG.warning(_LW('No auth_plugin configured in %s'),
 | 
				
			||||||
 | 
					                                AUTH_GROUP)
 | 
				
			||||||
 | 
					                session = ksc_session.Session.load_from_conf_options(
 | 
				
			||||||
 | 
					                    cfg.CONF, AUTH_GROUP, auth=auth)
 | 
				
			||||||
 | 
					                LOG.debug("Got session: %s" % session)
 | 
				
			||||||
 | 
					                self.keystone = ksc_client.Client(session=session)
 | 
				
			||||||
 | 
					                LOG.debug("Got client: %s" % self.keystone)
 | 
				
			||||||
 | 
					            LOG.debug("Calling project API")
 | 
				
			||||||
 | 
					            projects = self.keystone.projects.list()
 | 
				
			||||||
 | 
					            LOG.debug("Received projects: %s" % projects)
 | 
				
			||||||
 | 
					            for project in projects:
 | 
				
			||||||
 | 
					                self.project_names[project.id] = project.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_project_name(self, project_id):
 | 
				
			||||||
 | 
					        """Get name of project from cache.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :param project_id: ID of the project
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Get the name of the project identified by project_id from the
 | 
				
			||||||
 | 
					        cache. If the cache contains project_id, the project's name is
 | 
				
			||||||
 | 
					        returned. If not, None is returned.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        return self.project_names.get(project_id)
 | 
				
			||||||
@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 neutron._i18n import _LI
 | 
				
			||||||
 | 
					from neutron.api import extensions
 | 
				
			||||||
 | 
					from neutron import manager as n_manager
 | 
				
			||||||
 | 
					from neutron.plugins.ml2 import driver_api
 | 
				
			||||||
 | 
					from oslo_log import log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import (
 | 
				
			||||||
 | 
					    extensions as extensions_pkg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = log.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ApicExtensionDriver(driver_api.ExtensionDriver):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM ED __init__"))
 | 
				
			||||||
 | 
					        self._mechanism_driver = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def initialize(self):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM ED initializing"))
 | 
				
			||||||
 | 
					        extensions.append_api_extensions_path(extensions_pkg.__path__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def _md(self):
 | 
				
			||||||
 | 
					        if not self._mechanism_driver:
 | 
				
			||||||
 | 
					            # REVISIT(rkukura): It might be safer to search the MDs by
 | 
				
			||||||
 | 
					            # class rather than index by name, or to use a class
 | 
				
			||||||
 | 
					            # variable to find the instance.
 | 
				
			||||||
 | 
					            plugin = n_manager.NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					            mech_mgr = plugin.mechanism_manager
 | 
				
			||||||
 | 
					            self._mechanism_driver = mech_mgr.mech_drivers['apic_aim'].obj
 | 
				
			||||||
 | 
					        return self._mechanism_driver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def extension_alias(self):
 | 
				
			||||||
 | 
					        return "cisco-apic"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extend_network_dict(self, session, base_model, result):
 | 
				
			||||||
 | 
					        self._md.extend_network_dict(session, base_model, result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extend_subnet_dict(self, session, base_model, result):
 | 
				
			||||||
 | 
					        self._md.extend_subnet_dict(session, base_model, result)
 | 
				
			||||||
@@ -0,0 +1,69 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 neutron.api import extensions
 | 
				
			||||||
 | 
					from neutron.api.v2 import attributes
 | 
				
			||||||
 | 
					from neutron.extensions import address_scope
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ALIAS = 'cisco-apic'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DIST_NAMES = 'apic:distinguished_names'
 | 
				
			||||||
 | 
					SYNC_STATE = 'apic:synchronization_state'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					BD = 'BridgeDomain'
 | 
				
			||||||
 | 
					CTX = 'Context'
 | 
				
			||||||
 | 
					EPG = 'EndpointGroup'
 | 
				
			||||||
 | 
					SUBNET = 'Subnet'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SYNC_SYNCED = 'synced'
 | 
				
			||||||
 | 
					SYNC_PENDING = 'pending'
 | 
				
			||||||
 | 
					SYNC_FAILED = 'failed'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					APIC_ATTRIBUTES = {
 | 
				
			||||||
 | 
					    DIST_NAMES: {'allow_post': False, 'allow_put': False, 'is_visible': True},
 | 
				
			||||||
 | 
					    SYNC_STATE: {'allow_post': False, 'allow_put': False, 'is_visible': True}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					EXTENDED_ATTRIBUTES_2_0 = {
 | 
				
			||||||
 | 
					    attributes.NETWORKS: APIC_ATTRIBUTES,
 | 
				
			||||||
 | 
					    attributes.SUBNETS: APIC_ATTRIBUTES,
 | 
				
			||||||
 | 
					    address_scope.ADDRESS_SCOPES: APIC_ATTRIBUTES
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Cisco_apic(extensions.ExtensionDescriptor):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_name(cls):
 | 
				
			||||||
 | 
					        return "Cisco APIC"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_alias(cls):
 | 
				
			||||||
 | 
					        return ALIAS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_description(cls):
 | 
				
			||||||
 | 
					        return ("Extension exposing mapping of Neutron resources to Cisco "
 | 
				
			||||||
 | 
					                "APIC constructs")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @classmethod
 | 
				
			||||||
 | 
					    def get_updated(cls):
 | 
				
			||||||
 | 
					        return "2016-03-31T12:00:00-00:00"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_extended_resources(self, version):
 | 
				
			||||||
 | 
					        if version == "2.0":
 | 
				
			||||||
 | 
					            return EXTENDED_ATTRIBUTES_2_0
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return {}
 | 
				
			||||||
@@ -0,0 +1,589 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 aim import aim_manager
 | 
				
			||||||
 | 
					from aim.api import resource as aim_resource
 | 
				
			||||||
 | 
					from aim import context as aim_context
 | 
				
			||||||
 | 
					from neutron._i18n import _LE
 | 
				
			||||||
 | 
					from neutron._i18n import _LI
 | 
				
			||||||
 | 
					from neutron._i18n import _LW
 | 
				
			||||||
 | 
					from neutron.agent.linux import dhcp
 | 
				
			||||||
 | 
					from neutron.common import constants as n_constants
 | 
				
			||||||
 | 
					from neutron.common import rpc as n_rpc
 | 
				
			||||||
 | 
					from neutron.db import models_v2
 | 
				
			||||||
 | 
					from neutron.extensions import portbindings
 | 
				
			||||||
 | 
					from neutron import manager
 | 
				
			||||||
 | 
					from neutron.plugins.ml2 import driver_api as api
 | 
				
			||||||
 | 
					from neutron.plugins.ml2 import rpc as ml2_rpc
 | 
				
			||||||
 | 
					from opflexagent import constants as ofcst
 | 
				
			||||||
 | 
					from opflexagent import rpc as o_rpc
 | 
				
			||||||
 | 
					from oslo_log import log
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from gbpservice.neutron.plugins.ml2plus import driver_api as api_plus
 | 
				
			||||||
 | 
					from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import apic_mapper
 | 
				
			||||||
 | 
					from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import cache
 | 
				
			||||||
 | 
					from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extensions import (
 | 
				
			||||||
 | 
					    cisco_apic)
 | 
				
			||||||
 | 
					from gbpservice.neutron.plugins.ml2plus.drivers.apic_aim import model
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = log.getLogger(__name__)
 | 
				
			||||||
 | 
					AP_NAME = 'NeutronAP'
 | 
				
			||||||
 | 
					AGENT_TYPE_DVS = 'DVS agent'
 | 
				
			||||||
 | 
					VIF_TYPE_DVS = 'dvs'
 | 
				
			||||||
 | 
					PROMISCUOUS_TYPES = [n_constants.DEVICE_OWNER_DHCP,
 | 
				
			||||||
 | 
					                     n_constants.DEVICE_OWNER_LOADBALANCER]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ApicMechanismDriver(api_plus.MechanismDriver):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD __init__"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def initialize(self):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD initializing"))
 | 
				
			||||||
 | 
					        self.project_name_cache = cache.ProjectNameCache()
 | 
				
			||||||
 | 
					        self.db = model.DbModel()
 | 
				
			||||||
 | 
					        self.name_mapper = apic_mapper.APICNameMapper(self.db, log)
 | 
				
			||||||
 | 
					        self.aim = aim_manager.AimManager()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # REVISIT(rkukura): Read from config or possibly from AIM?
 | 
				
			||||||
 | 
					        self.enable_dhcp_opt = True
 | 
				
			||||||
 | 
					        self.enable_metadata_opt = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self._setup_opflex_rpc_listeners()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _setup_opflex_rpc_listeners(self):
 | 
				
			||||||
 | 
					        self.opflex_endpoints = [o_rpc.GBPServerRpcCallback(self)]
 | 
				
			||||||
 | 
					        self.opflex_topic = o_rpc.TOPIC_OPFLEX
 | 
				
			||||||
 | 
					        self.opflex_conn = n_rpc.create_connection(new=True)
 | 
				
			||||||
 | 
					        self.opflex_conn.create_consumer(
 | 
				
			||||||
 | 
					            self.opflex_topic, self.opflex_endpoints, fanout=False)
 | 
				
			||||||
 | 
					        self.opflex_conn.consume_in_threads()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def ensure_tenant(self, plugin_context, tenant_id):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD ensuring tenant_id: %s"), tenant_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.project_name_cache.ensure_project(tenant_id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Move the following to precommit methods so
 | 
				
			||||||
 | 
					        # AIM tenants and application profiles are created whenever
 | 
				
			||||||
 | 
					        # needed.
 | 
				
			||||||
 | 
					        session = plugin_context.session
 | 
				
			||||||
 | 
					        with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					            project_name = self.project_name_cache.get_project_name(tenant_id)
 | 
				
			||||||
 | 
					            tenant_name = self.name_mapper.tenant(session, tenant_id,
 | 
				
			||||||
 | 
					                                                  project_name)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tenant = aim_resource.Tenant(name=tenant_name)
 | 
				
			||||||
 | 
					            if not self.aim.get(aim_ctx, tenant):
 | 
				
			||||||
 | 
					                self.aim.create(aim_ctx, tenant)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            ap = aim_resource.ApplicationProfile(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                                 name=AP_NAME)
 | 
				
			||||||
 | 
					            if not self.aim.get(aim_ctx, ap):
 | 
				
			||||||
 | 
					                self.aim.create(aim_ctx, ap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_network_precommit(self, context):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD creating network: %s"), context.current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        session = context._plugin_context.session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tenant_id = context.current['tenant_id']
 | 
				
			||||||
 | 
					        tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                 {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        id = context.current['id']
 | 
				
			||||||
 | 
					        name = context.current['name']
 | 
				
			||||||
 | 
					        bd_name = self.name_mapper.network(session, id, name)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Mapped network_id %(id)s with name %(name)s to "
 | 
				
			||||||
 | 
					                     "%(apic_name)s"),
 | 
				
			||||||
 | 
					                 {'id': id, 'name': name, 'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bd = aim_resource.BridgeDomain(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                       name=bd_name)
 | 
				
			||||||
 | 
					        self.aim.create(aim_ctx, bd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        epg = aim_resource.EndpointGroup(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                         app_profile_name=AP_NAME,
 | 
				
			||||||
 | 
					                                         name=bd_name)
 | 
				
			||||||
 | 
					        self.aim.create(aim_ctx, epg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_network_precommit(self, context):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD deleting network: %s"), context.current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        session = context._plugin_context.session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tenant_id = context.current['tenant_id']
 | 
				
			||||||
 | 
					        tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                 {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        id = context.current['id']
 | 
				
			||||||
 | 
					        bd_name = self.name_mapper.network(session, id)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Mapped network_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                 {'id': id, 'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        epg = aim_resource.EndpointGroup(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                         app_profile_name=AP_NAME,
 | 
				
			||||||
 | 
					                                         name=bd_name)
 | 
				
			||||||
 | 
					        self.aim.delete(aim_ctx, epg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bd = aim_resource.BridgeDomain(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                       name=bd_name)
 | 
				
			||||||
 | 
					        self.aim.delete(aim_ctx, bd)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.name_mapper.delete_apic_name(session, id)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extend_network_dict(self, session, base_model, result):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD extending dict for network: %s"), result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        sync_state = cisco_apic.SYNC_SYNCED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        tenant_id = result['tenant_id']
 | 
				
			||||||
 | 
					        tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                 {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        id = result['id']
 | 
				
			||||||
 | 
					        name = result['name']
 | 
				
			||||||
 | 
					        bd_name = self.name_mapper.network(session, id, name)
 | 
				
			||||||
 | 
					        LOG.info(_LI("Mapped network_id %(id)s with name %(name)s to "
 | 
				
			||||||
 | 
					                     "%(apic_name)s"),
 | 
				
			||||||
 | 
					                 {'id': id, 'name': name, 'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bd = aim_resource.BridgeDomain(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                       name=bd_name)
 | 
				
			||||||
 | 
					        bd = self.aim.get(aim_ctx, bd)
 | 
				
			||||||
 | 
					        LOG.debug("got BD with DN: %s", bd.dn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        epg = aim_resource.EndpointGroup(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                         app_profile_name=AP_NAME,
 | 
				
			||||||
 | 
					                                         name=bd_name)
 | 
				
			||||||
 | 
					        epg = self.aim.get(aim_ctx, epg)
 | 
				
			||||||
 | 
					        LOG.debug("got EPG with DN: %s", epg.dn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[cisco_apic.DIST_NAMES] = {cisco_apic.BD: bd.dn,
 | 
				
			||||||
 | 
					                                         cisco_apic.EPG: epg.dn}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bd_status = self.aim.get_status(aim_ctx, bd)
 | 
				
			||||||
 | 
					        self._merge_status(sync_state, bd_status)
 | 
				
			||||||
 | 
					        epg_status = self.aim.get_status(aim_ctx, epg)
 | 
				
			||||||
 | 
					        self._merge_status(sync_state, epg_status)
 | 
				
			||||||
 | 
					        result[cisco_apic.SYNC_STATE] = sync_state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def create_subnet_precommit(self, context):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD creating subnet: %s"), context.current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # REVISIT(rkukura): Do we need to do any of the
 | 
				
			||||||
 | 
					        # constraints/scope stuff?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gateway_ip_mask = self._gateway_ip_mask(context.current)
 | 
				
			||||||
 | 
					        if gateway_ip_mask:
 | 
				
			||||||
 | 
					            session = context._plugin_context.session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_id = context.current['network_id']
 | 
				
			||||||
 | 
					            # REVISIT(rkukura): Should Ml2Plus extend SubnetContext
 | 
				
			||||||
 | 
					            # with network?
 | 
				
			||||||
 | 
					            network = (session.query(models_v2.Network).
 | 
				
			||||||
 | 
					                       filter_by(id=network_id).
 | 
				
			||||||
 | 
					                       one())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tenant_id = network.tenant_id
 | 
				
			||||||
 | 
					            tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_name = network.name
 | 
				
			||||||
 | 
					            bd_name = self.name_mapper.network(session, network_id,
 | 
				
			||||||
 | 
					                                               network_name)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped network_id %(id)s with name %(name)s to "
 | 
				
			||||||
 | 
					                         "%(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': network_id, 'name': network_name,
 | 
				
			||||||
 | 
					                      'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            subnet = aim_resource.Subnet(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                         bd_name=bd_name,
 | 
				
			||||||
 | 
					                                         gw_ip_mask=gateway_ip_mask)
 | 
				
			||||||
 | 
					            subnet = self.aim.create(aim_ctx, subnet)
 | 
				
			||||||
 | 
					            subnet_dn = subnet.dn
 | 
				
			||||||
 | 
					            subnet_status = self.aim.get_status(aim_ctx, subnet)
 | 
				
			||||||
 | 
					            sync_state = cisco_apic.SYNC_SYNCED
 | 
				
			||||||
 | 
					            self._merge_status(sync_state, subnet_status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # ML2 does not extend subnet dict after precommit.
 | 
				
			||||||
 | 
					            context.current[cisco_apic.DIST_NAMES] = {cisco_apic.SUBNET:
 | 
				
			||||||
 | 
					                                                      subnet_dn}
 | 
				
			||||||
 | 
					            context.current[cisco_apic.SYNC_STATE] = sync_state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def update_subnet_precommit(self, context):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD updating subnet: %s"), context.current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if context.current['gateway_ip'] != context.original['gateway_ip']:
 | 
				
			||||||
 | 
					            session = context._plugin_context.session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_id = context.current['network_id']
 | 
				
			||||||
 | 
					            # REVISIT(rkukura): Should Ml2Plus extend SubnetContext
 | 
				
			||||||
 | 
					            # with network?
 | 
				
			||||||
 | 
					            network = (session.query(models_v2.Network).
 | 
				
			||||||
 | 
					                       filter_by(id=network_id).
 | 
				
			||||||
 | 
					                       one())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tenant_id = network.tenant_id
 | 
				
			||||||
 | 
					            tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_name = network.name
 | 
				
			||||||
 | 
					            bd_name = self.name_mapper.network(session, network_id,
 | 
				
			||||||
 | 
					                                               network_name)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped network_id %(id)s with name %(name)s to "
 | 
				
			||||||
 | 
					                         "%(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': network_id, 'name': network_name,
 | 
				
			||||||
 | 
					                      'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            gateway_ip_mask = self._gateway_ip_mask(context.original)
 | 
				
			||||||
 | 
					            if gateway_ip_mask:
 | 
				
			||||||
 | 
					                subnet = aim_resource.Subnet(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                             bd_name=bd_name,
 | 
				
			||||||
 | 
					                                             gw_ip_mask=gateway_ip_mask)
 | 
				
			||||||
 | 
					                self.aim.delete(aim_ctx, subnet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            gateway_ip_mask = self._gateway_ip_mask(context.current)
 | 
				
			||||||
 | 
					            if gateway_ip_mask:
 | 
				
			||||||
 | 
					                subnet = aim_resource.Subnet(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                             bd_name=bd_name,
 | 
				
			||||||
 | 
					                                             gw_ip_mask=gateway_ip_mask)
 | 
				
			||||||
 | 
					                subnet = self.aim.create(aim_ctx, subnet)
 | 
				
			||||||
 | 
					                subnet_dn = subnet.dn
 | 
				
			||||||
 | 
					                subnet_status = self.aim.get_status(aim_ctx, subnet)
 | 
				
			||||||
 | 
					                sync_state = cisco_apic.SYNC_SYNCED
 | 
				
			||||||
 | 
					                self._merge_status(sync_state, subnet_status)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                # ML2 does not extend subnet dict after precommit.
 | 
				
			||||||
 | 
					                context.current[cisco_apic.DIST_NAMES] = {cisco_apic.SUBNET:
 | 
				
			||||||
 | 
					                                                          subnet_dn}
 | 
				
			||||||
 | 
					                context.current[cisco_apic.SYNC_STATE] = sync_state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_subnet_precommit(self, context):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD deleting subnet: %s"), context.current)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gateway_ip_mask = self._gateway_ip_mask(context.current)
 | 
				
			||||||
 | 
					        if gateway_ip_mask:
 | 
				
			||||||
 | 
					            session = context._plugin_context.session
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_id = context.current['network_id']
 | 
				
			||||||
 | 
					            # REVISIT(rkukura): Should Ml2Plus extend SubnetContext
 | 
				
			||||||
 | 
					            # with network?
 | 
				
			||||||
 | 
					            network = (session.query(models_v2.Network).
 | 
				
			||||||
 | 
					                       filter_by(id=network_id).
 | 
				
			||||||
 | 
					                       one())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tenant_id = network.tenant_id
 | 
				
			||||||
 | 
					            tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_name = network.name
 | 
				
			||||||
 | 
					            bd_name = self.name_mapper.network(session, network_id,
 | 
				
			||||||
 | 
					                                               network_name)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped network_id %(id)s with name %(name)s to "
 | 
				
			||||||
 | 
					                         "%(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': network_id, 'name': network_name,
 | 
				
			||||||
 | 
					                      'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            subnet = aim_resource.Subnet(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                         bd_name=bd_name,
 | 
				
			||||||
 | 
					                                         gw_ip_mask=gateway_ip_mask)
 | 
				
			||||||
 | 
					            self.aim.delete(aim_ctx, subnet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def extend_subnet_dict(self, session, base_model, result):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM MD extending dict for subnet: %s"), result)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        subnet_dn = None
 | 
				
			||||||
 | 
					        sync_state = cisco_apic.SYNC_SYNCED
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        gateway_ip_mask = self._gateway_ip_mask(result)
 | 
				
			||||||
 | 
					        if gateway_ip_mask:
 | 
				
			||||||
 | 
					            network_id = result['network_id']
 | 
				
			||||||
 | 
					            network = (session.query(models_v2.Network).
 | 
				
			||||||
 | 
					                       filter_by(id=network_id).
 | 
				
			||||||
 | 
					                       one())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            tenant_id = network.tenant_id
 | 
				
			||||||
 | 
					            tenant_name = self.name_mapper.tenant(session, tenant_id)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped tenant_id %(id)s to %(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': tenant_id, 'apic_name': tenant_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network_name = network.name
 | 
				
			||||||
 | 
					            bd_name = self.name_mapper.network(session, network_id,
 | 
				
			||||||
 | 
					                                               network_name)
 | 
				
			||||||
 | 
					            LOG.info(_LI("Mapped network_id %(id)s with name %(name)s to "
 | 
				
			||||||
 | 
					                         "%(apic_name)s"),
 | 
				
			||||||
 | 
					                     {'id': network_id, 'name': network_name,
 | 
				
			||||||
 | 
					                      'apic_name': bd_name})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            aim_ctx = aim_context.AimContext(session)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            subnet = aim_resource.Subnet(tenant_name=tenant_name,
 | 
				
			||||||
 | 
					                                         bd_name=bd_name,
 | 
				
			||||||
 | 
					                                         gw_ip_mask=gateway_ip_mask)
 | 
				
			||||||
 | 
					            subnet = self.aim.get(aim_ctx, subnet)
 | 
				
			||||||
 | 
					            if subnet:
 | 
				
			||||||
 | 
					                LOG.debug("got Subnet with DN: %s", subnet.dn)
 | 
				
			||||||
 | 
					                subnet_dn = subnet.dn
 | 
				
			||||||
 | 
					                subnet_status = self.aim.get_status(aim_ctx, subnet)
 | 
				
			||||||
 | 
					                self._merge_status(sync_state, subnet_status)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                # This should always get replaced with the real DN
 | 
				
			||||||
 | 
					                # during precommit.
 | 
				
			||||||
 | 
					                subnet_dn = "AIM Subnet not yet created"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        result[cisco_apic.DIST_NAMES] = {cisco_apic.SUBNET: subnet_dn}
 | 
				
			||||||
 | 
					        result[cisco_apic.SYNC_STATE] = sync_state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def bind_port(self, context):
 | 
				
			||||||
 | 
					        LOG.debug("Attempting to bind port %(port)s on network %(net)s",
 | 
				
			||||||
 | 
					                  {'port': context.current['id'],
 | 
				
			||||||
 | 
					                   'net': context.network.current['id']})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Add support for baremetal hosts, SR-IOV and
 | 
				
			||||||
 | 
					        # other situations requiring dynamic segments.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Check the VNIC type.
 | 
				
			||||||
 | 
					        vnic_type = context.current.get(portbindings.VNIC_TYPE,
 | 
				
			||||||
 | 
					                                        portbindings.VNIC_NORMAL)
 | 
				
			||||||
 | 
					        if vnic_type not in [portbindings.VNIC_NORMAL]:
 | 
				
			||||||
 | 
					            LOG.debug("Refusing to bind due to unsupported vnic_type: %s",
 | 
				
			||||||
 | 
					                      vnic_type)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # For compute ports, try to bind DVS agent first.
 | 
				
			||||||
 | 
					        if context.current['device_owner'].startswith('compute:'):
 | 
				
			||||||
 | 
					            if self._agent_bind_port(context, AGENT_TYPE_DVS,
 | 
				
			||||||
 | 
					                                     self._dvs_bind_port):
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Try to bind OpFlex agent.
 | 
				
			||||||
 | 
					        self._agent_bind_port(context, ofcst.AGENT_TYPE_OPFLEX_OVS,
 | 
				
			||||||
 | 
					                              self._opflex_bind_port)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _agent_bind_port(self, context, agent_type, bind_strategy):
 | 
				
			||||||
 | 
					        for agent in context.host_agents(agent_type):
 | 
				
			||||||
 | 
					            LOG.debug("Checking agent: %s", agent)
 | 
				
			||||||
 | 
					            if agent['alive']:
 | 
				
			||||||
 | 
					                for segment in context.segments_to_bind:
 | 
				
			||||||
 | 
					                    if bind_strategy(context, segment, agent):
 | 
				
			||||||
 | 
					                        LOG.debug("Bound using segment: %s", segment)
 | 
				
			||||||
 | 
					            else:
 | 
				
			||||||
 | 
					                LOG.warning(_LW("Refusing to bind port %(port)s to dead "
 | 
				
			||||||
 | 
					                                "agent: %(agent)s"),
 | 
				
			||||||
 | 
					                            {'port': context.current['id'], 'agent': agent})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _opflex_bind_port(self, context, segment, agent):
 | 
				
			||||||
 | 
					        network_type = segment[api.NETWORK_TYPE]
 | 
				
			||||||
 | 
					        if network_type == ofcst.TYPE_OPFLEX:
 | 
				
			||||||
 | 
					            opflex_mappings = agent['configurations'].get('opflex_networks')
 | 
				
			||||||
 | 
					            LOG.debug("Checking segment: %(segment)s "
 | 
				
			||||||
 | 
					                      "for physical network: %(mappings)s ",
 | 
				
			||||||
 | 
					                      {'segment': segment, 'mappings': opflex_mappings})
 | 
				
			||||||
 | 
					            if (opflex_mappings is not None and
 | 
				
			||||||
 | 
					                segment[api.PHYSICAL_NETWORK] not in opflex_mappings):
 | 
				
			||||||
 | 
					                return False
 | 
				
			||||||
 | 
					        elif network_type != 'local':
 | 
				
			||||||
 | 
					            return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context.set_binding(segment[api.ID],
 | 
				
			||||||
 | 
					                            portbindings.VIF_TYPE_OVS,
 | 
				
			||||||
 | 
					                            {portbindings.CAP_PORT_FILTER: False,
 | 
				
			||||||
 | 
					                             portbindings.OVS_HYBRID_PLUG: False})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _dvs_bind_port(self, context, segment, agent):
 | 
				
			||||||
 | 
					        # TODO(rkukura): Implement DVS port binding
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # RPC Method
 | 
				
			||||||
 | 
					    def get_gbp_details(self, context, **kwargs):
 | 
				
			||||||
 | 
					        LOG.debug("APIC AIM MD handling get_gbp_details for: %s", kwargs)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            return self._get_gbp_details(context, kwargs)
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            device = kwargs.get('device')
 | 
				
			||||||
 | 
					            LOG.error(_LE("An exception has occurred while retrieving device "
 | 
				
			||||||
 | 
					                          "gbp details for %s"), device)
 | 
				
			||||||
 | 
					            LOG.exception(e)
 | 
				
			||||||
 | 
					            return {'device': device}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def request_endpoint_details(self, context, **kwargs):
 | 
				
			||||||
 | 
					        LOG.debug("APIC AIM MD handling get_endpoint_details for: %s", kwargs)
 | 
				
			||||||
 | 
					        try:
 | 
				
			||||||
 | 
					            request = kwargs.get('request')
 | 
				
			||||||
 | 
					            result = {'device': request['device'],
 | 
				
			||||||
 | 
					                      'timestamp': request['timestamp'],
 | 
				
			||||||
 | 
					                      'request_id': request['request_id'],
 | 
				
			||||||
 | 
					                      'gbp_details': None,
 | 
				
			||||||
 | 
					                      'neutron_details': None}
 | 
				
			||||||
 | 
					            result['gbp_details'] = self._get_gbp_details(context, request)
 | 
				
			||||||
 | 
					            result['neutron_details'] = ml2_rpc.RpcCallbacks(
 | 
				
			||||||
 | 
					                None, None).get_device_details(context, **request)
 | 
				
			||||||
 | 
					            return result
 | 
				
			||||||
 | 
					        except Exception as e:
 | 
				
			||||||
 | 
					            LOG.error(_LE("An exception has occurred while requesting device "
 | 
				
			||||||
 | 
					                          "gbp details for %s"), request.get('device'))
 | 
				
			||||||
 | 
					            LOG.exception(e)
 | 
				
			||||||
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_gbp_details(self, context, request):
 | 
				
			||||||
 | 
					        device = request.get('device')
 | 
				
			||||||
 | 
					        host = request.get('host')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        core_plugin = manager.NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					        port_id = core_plugin._device_to_port_id(context, device)
 | 
				
			||||||
 | 
					        port_context = core_plugin.get_bound_port_context(context, port_id,
 | 
				
			||||||
 | 
					                                                          host)
 | 
				
			||||||
 | 
					        if not port_context:
 | 
				
			||||||
 | 
					            LOG.warning(_LW("Device %(device)s requested by agent "
 | 
				
			||||||
 | 
					                            "%(agent_id)s not found in database"),
 | 
				
			||||||
 | 
					                        {'device': port_id,
 | 
				
			||||||
 | 
					                         'agent_id': request.get('agent_id')})
 | 
				
			||||||
 | 
					            return {'device': device}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        port = port_context.current
 | 
				
			||||||
 | 
					        if port[portbindings.HOST_ID] != host:
 | 
				
			||||||
 | 
					            LOG.warning(_LW("Device %(device)s requested by agent "
 | 
				
			||||||
 | 
					                            "%(agent_id)s not found bound for host %(host)s"),
 | 
				
			||||||
 | 
					                        {'device': port_id, 'host': host,
 | 
				
			||||||
 | 
					                         'agent_id': request.get('agent_id')})
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        session = context.session
 | 
				
			||||||
 | 
					        with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					            # REVISIT(rkukura): Should AIM resources be
 | 
				
			||||||
 | 
					            # validated/created here if necessary? Also need to avoid
 | 
				
			||||||
 | 
					            # creating any new name mappings without first getting
 | 
				
			||||||
 | 
					            # their resource names.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # TODO(rkukura): For GBP, we need to use the EPG
 | 
				
			||||||
 | 
					            # associated with the port's PT's PTG. For now, we just use the
 | 
				
			||||||
 | 
					            # network's default EPG.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # TODO(rkukura): Use common tenant for shared networks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # TODO(rkukura): Scope the tenant's AIM name.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            network = port_context.network.current
 | 
				
			||||||
 | 
					            epg_tenant_name = self.name_mapper.tenant(session,
 | 
				
			||||||
 | 
					                                                      network['tenant_id'])
 | 
				
			||||||
 | 
					            epg_name = self.name_mapper.network(session, network['id'], None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        promiscuous_mode = port['device_owner'] in PROMISCUOUS_TYPES
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        details = {'allowed_address_pairs': port['allowed_address_pairs'],
 | 
				
			||||||
 | 
					                   'app_profile_name': AP_NAME,
 | 
				
			||||||
 | 
					                   'device': device,
 | 
				
			||||||
 | 
					                   'enable_dhcp_optimization': self.enable_dhcp_opt,
 | 
				
			||||||
 | 
					                   'enable_metadata_optimization': self.enable_metadata_opt,
 | 
				
			||||||
 | 
					                   'endpoint_group_name': epg_name,
 | 
				
			||||||
 | 
					                   'host': host,
 | 
				
			||||||
 | 
					                   'l3_policy_id': network['tenant_id'],  # TODO(rkukura)
 | 
				
			||||||
 | 
					                   'mac_address': port['mac_address'],
 | 
				
			||||||
 | 
					                   'port_id': port_id,
 | 
				
			||||||
 | 
					                   'promiscuous_mode': promiscuous_mode,
 | 
				
			||||||
 | 
					                   'ptg_tenant': epg_tenant_name,
 | 
				
			||||||
 | 
					                   'subnets': self._get_subnet_details(core_plugin, context,
 | 
				
			||||||
 | 
					                                                       port)}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if port['device_owner'].startswith('compute:') and port['device_id']:
 | 
				
			||||||
 | 
					            # REVISIT(rkukura): Do we need to map to name using nova client?
 | 
				
			||||||
 | 
					            details['vm-name'] = port['device_id']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Mark active allowed_address_pairs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Add the following details common to the old
 | 
				
			||||||
 | 
					        # GBP and ML2 drivers: floating_ip, host_snat_ips, ip_mapping,
 | 
				
			||||||
 | 
					        # vrf_name, vrf_subnets, vrf_tenant.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Add the following details unique to the old
 | 
				
			||||||
 | 
					        # ML2 driver: attestation, interface_mtu.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Add the following details unique to the old
 | 
				
			||||||
 | 
					        # GBP driver: extra_details, extra_ips, fixed_ips,
 | 
				
			||||||
 | 
					        # l2_policy_id.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return details
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _get_subnet_details(self, core_plugin, context, port):
 | 
				
			||||||
 | 
					        subnets = core_plugin.get_subnets(
 | 
				
			||||||
 | 
					            context,
 | 
				
			||||||
 | 
					            filters={'id': [ip['subnet_id'] for ip in port['fixed_ips']]})
 | 
				
			||||||
 | 
					        for subnet in subnets:
 | 
				
			||||||
 | 
					            dhcp_ips = set()
 | 
				
			||||||
 | 
					            for port in core_plugin.get_ports(
 | 
				
			||||||
 | 
					                    context, filters={
 | 
				
			||||||
 | 
					                        'network_id': [subnet['network_id']],
 | 
				
			||||||
 | 
					                        'device_owner': [n_constants.DEVICE_OWNER_DHCP]}):
 | 
				
			||||||
 | 
					                dhcp_ips |= set([x['ip_address'] for x in port['fixed_ips']
 | 
				
			||||||
 | 
					                                 if x['subnet_id'] == subnet['id']])
 | 
				
			||||||
 | 
					            dhcp_ips = list(dhcp_ips)
 | 
				
			||||||
 | 
					            if not subnet['dns_nameservers']:
 | 
				
			||||||
 | 
					                # Use DHCP namespace port IP
 | 
				
			||||||
 | 
					                subnet['dns_nameservers'] = dhcp_ips
 | 
				
			||||||
 | 
					            # Ser Default route if needed
 | 
				
			||||||
 | 
					            metadata = default = False
 | 
				
			||||||
 | 
					            if subnet['ip_version'] == 4:
 | 
				
			||||||
 | 
					                for route in subnet['host_routes']:
 | 
				
			||||||
 | 
					                    if route['destination'] == '0.0.0.0/0':
 | 
				
			||||||
 | 
					                        default = True
 | 
				
			||||||
 | 
					                    if route['destination'] == dhcp.METADATA_DEFAULT_CIDR:
 | 
				
			||||||
 | 
					                        metadata = True
 | 
				
			||||||
 | 
					                # Set missing routes
 | 
				
			||||||
 | 
					                if not default:
 | 
				
			||||||
 | 
					                    subnet['host_routes'].append(
 | 
				
			||||||
 | 
					                        {'destination': '0.0.0.0/0',
 | 
				
			||||||
 | 
					                         'nexthop': subnet['gateway_ip']})
 | 
				
			||||||
 | 
					                if not metadata and dhcp_ips and not self.enable_metadata_opt:
 | 
				
			||||||
 | 
					                    subnet['host_routes'].append(
 | 
				
			||||||
 | 
					                        {'destination': dhcp.METADATA_DEFAULT_CIDR,
 | 
				
			||||||
 | 
					                         'nexthop': dhcp_ips[0]})
 | 
				
			||||||
 | 
					            subnet['dhcp_server_ips'] = dhcp_ips
 | 
				
			||||||
 | 
					        return subnets
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _merge_status(self, sync_state, status):
 | 
				
			||||||
 | 
					        if status.is_error():
 | 
				
			||||||
 | 
					            sync_state = cisco_apic.SYNC_ERROR
 | 
				
			||||||
 | 
					        elif status.is_build() and sync_state is not cisco_apic.SYNC_ERROR:
 | 
				
			||||||
 | 
					            sync_state = cisco_apic.SYNC_BUILD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _gateway_ip_mask(self, subnet):
 | 
				
			||||||
 | 
					        gateway_ip = subnet['gateway_ip']
 | 
				
			||||||
 | 
					        if gateway_ip:
 | 
				
			||||||
 | 
					            prefix_len = subnet['cidr'].split('/')[1]
 | 
				
			||||||
 | 
					            return gateway_ip + '/' + prefix_len
 | 
				
			||||||
							
								
								
									
										63
									
								
								gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/model.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								gbpservice/neutron/plugins/ml2plus/drivers/apic_aim/model.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 apic_ml2.neutron.plugins.ml2.drivers.cisco.apic import (
 | 
				
			||||||
 | 
					    apic_model as old_model)
 | 
				
			||||||
 | 
					from neutron._i18n import _LI
 | 
				
			||||||
 | 
					from oslo_log import log
 | 
				
			||||||
 | 
					from sqlalchemy import orm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LOG = log.getLogger(__name__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# REVISIT(rkukura): Temporarily using ApicName model defined in old
 | 
				
			||||||
 | 
					# apic-ml2 driver with migration in neutron. We should define our
 | 
				
			||||||
 | 
					# own, and may want to switch to per-resource name mapping tables with
 | 
				
			||||||
 | 
					# foriegn keys.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class DbModel(object):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self):
 | 
				
			||||||
 | 
					        LOG.info(_LI("APIC AIM DbModel __init__"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_apic_name(self, session, neutron_id, neutron_type, apic_name):
 | 
				
			||||||
 | 
					        name = old_model.ApicName(neutron_id=neutron_id,
 | 
				
			||||||
 | 
					                                  neutron_type=neutron_type,
 | 
				
			||||||
 | 
					                                  apic_name=apic_name)
 | 
				
			||||||
 | 
					        with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					            session.add(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_apic_name(self, session, neutron_id, neutron_type):
 | 
				
			||||||
 | 
					        return session.query(old_model.ApicName.apic_name).filter_by(
 | 
				
			||||||
 | 
					            neutron_id=neutron_id, neutron_type=neutron_type).first()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def delete_apic_name(self, session, neutron_id):
 | 
				
			||||||
 | 
					        with session.begin(subtransactions=True):
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                session.query(old_model.ApicName).filter_by(
 | 
				
			||||||
 | 
					                    neutron_id=neutron_id).delete()
 | 
				
			||||||
 | 
					            except orm.exc.NoResultFound:
 | 
				
			||||||
 | 
					                return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_filtered_apic_names(self, session, neutron_id=None,
 | 
				
			||||||
 | 
					                                neutron_type=None, apic_name=None):
 | 
				
			||||||
 | 
					        query = session.query(old_model.ApicName.apic_name)
 | 
				
			||||||
 | 
					        if neutron_id:
 | 
				
			||||||
 | 
					            query = query.filter_by(neutron_id=neutron_id)
 | 
				
			||||||
 | 
					        if neutron_type:
 | 
				
			||||||
 | 
					            query = query.filter_by(neutron_type=neutron_type)
 | 
				
			||||||
 | 
					        if apic_name:
 | 
				
			||||||
 | 
					            query = query.filter_by(apic_name=apic_name)
 | 
				
			||||||
 | 
					        return query.all()
 | 
				
			||||||
							
								
								
									
										294
									
								
								gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										294
									
								
								gbpservice/neutron/tests/unit/plugins/ml2plus/test_apic_aim.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,294 @@
 | 
				
			|||||||
 | 
					# Copyright (c) 2016 Cisco Systems Inc.
 | 
				
			||||||
 | 
					# 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 aim.db import model_base as aim_model_base
 | 
				
			||||||
 | 
					from keystoneclient.v3 import client as ksc_client
 | 
				
			||||||
 | 
					from neutron import context
 | 
				
			||||||
 | 
					from neutron.db import api as db_api
 | 
				
			||||||
 | 
					from neutron import manager
 | 
				
			||||||
 | 
					from neutron.plugins.ml2 import config
 | 
				
			||||||
 | 
					from neutron.tests.unit.db import test_db_base_plugin_v2 as test_plugin
 | 
				
			||||||
 | 
					from opflexagent import constants as ofcst
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PLUGIN_NAME = 'gbpservice.neutron.plugins.ml2plus.plugin.Ml2PlusPlugin'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AGENT_CONF_OPFLEX = {'alive': True, 'binary': 'somebinary',
 | 
				
			||||||
 | 
					                     'topic': 'sometopic',
 | 
				
			||||||
 | 
					                     'agent_type': ofcst.AGENT_TYPE_OPFLEX_OVS,
 | 
				
			||||||
 | 
					                     'configurations': {
 | 
				
			||||||
 | 
					                         'opflex_networks': None,
 | 
				
			||||||
 | 
					                         'bridge_mappings': {'physnet1': 'br-eth1'}}}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# REVISIT(rkukura): Use mock for this instead?
 | 
				
			||||||
 | 
					class FakeTenant(object):
 | 
				
			||||||
 | 
					    def __init__(self, id, name):
 | 
				
			||||||
 | 
					        self.id = id
 | 
				
			||||||
 | 
					        self.name = name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FakeProjectManager(object):
 | 
				
			||||||
 | 
					    def list(self):
 | 
				
			||||||
 | 
					        return [FakeTenant('test-tenant', 'TestTenantName'),
 | 
				
			||||||
 | 
					                FakeTenant('bad_tenant_id', 'BadTenantName')]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class FakeKeystoneClient(object):
 | 
				
			||||||
 | 
					    def __init__(self, **kwargs):
 | 
				
			||||||
 | 
					        self.projects = FakeProjectManager()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ApicAimTestCase(test_plugin.NeutronDbPluginV2TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def setUp(self):
 | 
				
			||||||
 | 
					        # Enable the test mechanism driver to ensure that
 | 
				
			||||||
 | 
					        # we can successfully call through to all mechanism
 | 
				
			||||||
 | 
					        # driver apis.
 | 
				
			||||||
 | 
					        config.cfg.CONF.set_override('mechanism_drivers',
 | 
				
			||||||
 | 
					                                     ['logger', 'apic_aim'],
 | 
				
			||||||
 | 
					                                     'ml2')
 | 
				
			||||||
 | 
					        config.cfg.CONF.set_override('extension_drivers',
 | 
				
			||||||
 | 
					                                     ['apic_aim'],
 | 
				
			||||||
 | 
					                                     'ml2')
 | 
				
			||||||
 | 
					        config.cfg.CONF.set_override('type_drivers',
 | 
				
			||||||
 | 
					                                     ['opflex', 'local', 'vlan'],
 | 
				
			||||||
 | 
					                                     'ml2')
 | 
				
			||||||
 | 
					        config.cfg.CONF.set_override('tenant_network_types',
 | 
				
			||||||
 | 
					                                     ['opflex'],
 | 
				
			||||||
 | 
					                                     'ml2')
 | 
				
			||||||
 | 
					        config.cfg.CONF.set_override('network_vlan_ranges',
 | 
				
			||||||
 | 
					                                     ['physnet1:1000:1099'],
 | 
				
			||||||
 | 
					                                     group='ml2_type_vlan')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        super(ApicAimTestCase, self).setUp(PLUGIN_NAME)
 | 
				
			||||||
 | 
					        self.port_create_status = 'DOWN'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.saved_keystone_client = ksc_client.Client
 | 
				
			||||||
 | 
					        ksc_client.Client = FakeKeystoneClient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        engine = db_api.get_engine()
 | 
				
			||||||
 | 
					        aim_model_base.Base.metadata.create_all(engine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.plugin = manager.NeutronManager.get_plugin()
 | 
				
			||||||
 | 
					        self.plugin.start_rpc_listeners()
 | 
				
			||||||
 | 
					        self.driver = self.plugin.mechanism_manager.mech_drivers[
 | 
				
			||||||
 | 
					            'apic_aim'].obj
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tearDown(self):
 | 
				
			||||||
 | 
					        ksc_client.Client = self.saved_keystone_client
 | 
				
			||||||
 | 
					        super(ApicAimTestCase, self).tearDown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestApicExtension(ApicAimTestCase):
 | 
				
			||||||
 | 
					    def _verify_dn(self, dist_names, key, mo_types, id):
 | 
				
			||||||
 | 
					        dn = dist_names.get(key)
 | 
				
			||||||
 | 
					        self.assertIsInstance(dn, basestring)
 | 
				
			||||||
 | 
					        self.assertEqual('uni/', dn[:4])
 | 
				
			||||||
 | 
					        for mo_type in mo_types:
 | 
				
			||||||
 | 
					            self.assertIn('/' + mo_type + '-', dn)
 | 
				
			||||||
 | 
					        self.assertIn(id, dn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _verify_no_dn(self, dist_names, key):
 | 
				
			||||||
 | 
					        self.assertIn(key, dist_names)
 | 
				
			||||||
 | 
					        self.assertIsNone(dist_names.get(key))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _verify_network_dist_names(self, net):
 | 
				
			||||||
 | 
					        id = net['id']
 | 
				
			||||||
 | 
					        dist_names = net.get('apic:distinguished_names')
 | 
				
			||||||
 | 
					        self.assertIsInstance(dist_names, dict)
 | 
				
			||||||
 | 
					        self._verify_dn(dist_names, 'BridgeDomain', ['tn', 'BD'], id[:5])
 | 
				
			||||||
 | 
					        self._verify_dn(dist_names, 'EndpointGroup', ['tn', 'ap', 'epg'],
 | 
				
			||||||
 | 
					                        id[:5])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_network(self):
 | 
				
			||||||
 | 
					        # Test create.
 | 
				
			||||||
 | 
					        net = self._make_network(self.fmt, 'net1', True)['network']
 | 
				
			||||||
 | 
					        net_id = net['id']
 | 
				
			||||||
 | 
					        self._verify_network_dist_names(net)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test show.
 | 
				
			||||||
 | 
					        res = self._show('networks', net_id)['network']
 | 
				
			||||||
 | 
					        self._verify_network_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test update.
 | 
				
			||||||
 | 
					        data = {'network': {'name': 'newnamefornet'}}
 | 
				
			||||||
 | 
					        res = self._update('networks', net_id, data)['network']
 | 
				
			||||||
 | 
					        self._verify_network_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _verify_subnet_dist_names(self, subnet):
 | 
				
			||||||
 | 
					        dist_names = subnet.get('apic:distinguished_names')
 | 
				
			||||||
 | 
					        self.assertIsInstance(dist_names, dict)
 | 
				
			||||||
 | 
					        if subnet['gateway_ip']:
 | 
				
			||||||
 | 
					            id = subnet['gateway_ip'] + '/' + subnet['cidr'].split('/')[1]
 | 
				
			||||||
 | 
					            self._verify_dn(dist_names, 'Subnet', ['tn', 'BD', 'subnet'], id)
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self._verify_no_dn(dist_names, 'Subnet')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_subnet_without_gw(self):
 | 
				
			||||||
 | 
					        # Test create without gateway.
 | 
				
			||||||
 | 
					        net = self._make_network(self.fmt, 'net', True)
 | 
				
			||||||
 | 
					        pools = [{'start': '10.0.0.2', 'end': '10.0.0.254'}]
 | 
				
			||||||
 | 
					        subnet = self._make_subnet(self.fmt, net, None,
 | 
				
			||||||
 | 
					                                   '10.0.0.0/24',
 | 
				
			||||||
 | 
					                                   allocation_pools=pools)['subnet']
 | 
				
			||||||
 | 
					        subnet_id = subnet['id']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(subnet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test show.
 | 
				
			||||||
 | 
					        res = self._show('subnets', subnet_id)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test update.
 | 
				
			||||||
 | 
					        data = {'subnet': {'name': 'newnameforsubnet'}}
 | 
				
			||||||
 | 
					        res = self._update('subnets', subnet_id, data)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test update adding gateay.
 | 
				
			||||||
 | 
					        data = {'subnet': {'gateway_ip': '10.0.0.1'}}
 | 
				
			||||||
 | 
					        res = self._update('subnets', subnet_id, data)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test show after adding gateway.
 | 
				
			||||||
 | 
					        res = self._show('subnets', subnet_id)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_subnet_with_gw(self):
 | 
				
			||||||
 | 
					        # Test create.
 | 
				
			||||||
 | 
					        net = self._make_network(self.fmt, 'net', True)
 | 
				
			||||||
 | 
					        subnet = self._make_subnet(self.fmt, net, '10.0.1.1',
 | 
				
			||||||
 | 
					                                   '10.0.1.0/24')['subnet']
 | 
				
			||||||
 | 
					        subnet_id = subnet['id']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(subnet)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test show.
 | 
				
			||||||
 | 
					        res = self._show('subnets', subnet_id)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test update.
 | 
				
			||||||
 | 
					        data = {'subnet': {'name': 'newnameforsubnet'}}
 | 
				
			||||||
 | 
					        res = self._update('subnets', subnet_id, data)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test update removing gateway.
 | 
				
			||||||
 | 
					        data = {'subnet': {'gateway_ip': None}}
 | 
				
			||||||
 | 
					        res = self._update('subnets', subnet_id, data)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Test show after removing gateway.
 | 
				
			||||||
 | 
					        res = self._show('subnets', subnet_id)['subnet']
 | 
				
			||||||
 | 
					        self._verify_subnet_dist_names(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestPortBinding(ApicAimTestCase):
 | 
				
			||||||
 | 
					    def _register_agent(self, host, agent_conf):
 | 
				
			||||||
 | 
					        agent = {'host': host}
 | 
				
			||||||
 | 
					        agent.update(agent_conf)
 | 
				
			||||||
 | 
					        self.plugin.create_or_update_agent(context.get_admin_context(), agent)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def _bind_port_to_host(self, port_id, host):
 | 
				
			||||||
 | 
					        data = {'port': {'binding:host_id': host,
 | 
				
			||||||
 | 
					                         'device_owner': 'compute:',
 | 
				
			||||||
 | 
					                         'device_id': 'someid'}}
 | 
				
			||||||
 | 
					        req = self.new_update_request('ports', data, port_id,
 | 
				
			||||||
 | 
					                                      self.fmt)
 | 
				
			||||||
 | 
					        return self.deserialize(self.fmt, req.get_response(self.api))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_bind_opflex_agent(self):
 | 
				
			||||||
 | 
					        self._register_agent('host1', AGENT_CONF_OPFLEX)
 | 
				
			||||||
 | 
					        net = self._make_network(self.fmt, 'net1', True)
 | 
				
			||||||
 | 
					        self._make_subnet(self.fmt, net, '10.0.1.1', '10.0.1.0/24')
 | 
				
			||||||
 | 
					        port = self._make_port(self.fmt, net['network']['id'])['port']
 | 
				
			||||||
 | 
					        port_id = port['id']
 | 
				
			||||||
 | 
					        port = self._bind_port_to_host(port_id, 'host1')['port']
 | 
				
			||||||
 | 
					        self.assertEqual('ovs', port['binding:vif_type'])
 | 
				
			||||||
 | 
					        self.assertEqual({'port_filter': False, 'ovs_hybrid_plug': False},
 | 
				
			||||||
 | 
					                         port['binding:vif_details'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Verify get_gbp_details.
 | 
				
			||||||
 | 
					        device = 'tap%s' % port_id
 | 
				
			||||||
 | 
					        details = self.driver.get_gbp_details(context.get_admin_context(),
 | 
				
			||||||
 | 
					            device=device, host='host1')
 | 
				
			||||||
 | 
					        self.assertEqual(port['allowed_address_pairs'],
 | 
				
			||||||
 | 
					                         details['allowed_address_pairs'])
 | 
				
			||||||
 | 
					        self.assertEqual('NeutronAP', details['app_profile_name'])
 | 
				
			||||||
 | 
					        self.assertEqual(device, details['device'])
 | 
				
			||||||
 | 
					        self.assertTrue(details['enable_dhcp_optimization'])
 | 
				
			||||||
 | 
					        self.assertTrue(details['enable_metadata_optimization'])
 | 
				
			||||||
 | 
					        self.assertIn('net1', details['endpoint_group_name'])
 | 
				
			||||||
 | 
					        self.assertEqual('host1', details['host'])
 | 
				
			||||||
 | 
					        self.assertEqual('test-tenant', details['l3_policy_id'])
 | 
				
			||||||
 | 
					        self.assertEqual(port['mac_address'], details['mac_address'])
 | 
				
			||||||
 | 
					        self.assertEqual(port_id, details['port_id'])
 | 
				
			||||||
 | 
					        self.assertFalse(details['promiscuous_mode'])
 | 
				
			||||||
 | 
					        self.assertIn('TestTenantName', details['ptg_tenant'])
 | 
				
			||||||
 | 
					        self.assertEqual(1, len(details['subnets']))
 | 
				
			||||||
 | 
					        self.assertEqual('someid', details['vm-name'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Verify request_endpoint_details.
 | 
				
			||||||
 | 
					        req_details = self.driver.request_endpoint_details(
 | 
				
			||||||
 | 
					            context.get_admin_context(),
 | 
				
			||||||
 | 
					            request={'device': 'tap%s' % port_id, 'host': 'host1',
 | 
				
			||||||
 | 
					                     'timestamp': 0, 'request_id': 'a_request_id'})
 | 
				
			||||||
 | 
					        self.assertEqual(details, req_details['gbp_details'])
 | 
				
			||||||
 | 
					        self.assertEqual(port_id, req_details['neutron_details']['port_id'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # TODO(rkukura): Verify subnet details. Also, test with
 | 
				
			||||||
 | 
					        # variations of DHCP IPs on subnet, dns_nameservers and
 | 
				
			||||||
 | 
					        # host_routes values, etc..
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO(rkukura): Add tests for promiscuous_mode cases.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_bind_unsupported_vnic_type(self):
 | 
				
			||||||
 | 
					        net = self._make_network(self.fmt, 'net1', True)
 | 
				
			||||||
 | 
					        self._make_subnet(self.fmt, net, '10.0.1.1', '10.0.1.0/24')
 | 
				
			||||||
 | 
					        vnic_arg = {'binding:vnic_type': 'macvtap'}
 | 
				
			||||||
 | 
					        port = self._make_port(self.fmt, net['network']['id'],
 | 
				
			||||||
 | 
					                               arg_list=('binding:vnic_type',),
 | 
				
			||||||
 | 
					                               **vnic_arg)['port']
 | 
				
			||||||
 | 
					        port = self._bind_port_to_host(port['id'], 'host1')['port']
 | 
				
			||||||
 | 
					        self.assertEqual('binding_failed', port['binding:vif_type'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # TODO(rkukura): Add tests for opflex, local and unsupported
 | 
				
			||||||
 | 
					    # network_type values.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestMl2BasicGet(test_plugin.TestBasicGet,
 | 
				
			||||||
 | 
					                      ApicAimTestCase):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestMl2V2HTTPResponse(test_plugin.TestV2HTTPResponse,
 | 
				
			||||||
 | 
					                            ApicAimTestCase):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestMl2PortsV2(test_plugin.TestPortsV2,
 | 
				
			||||||
 | 
					                     ApicAimTestCase):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestMl2NetworksV2(test_plugin.TestNetworksV2,
 | 
				
			||||||
 | 
					                        ApicAimTestCase):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestMl2SubnetsV2(test_plugin.TestSubnetsV2,
 | 
				
			||||||
 | 
					                       ApicAimTestCase):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestMl2SubnetPoolsV2(test_plugin.TestSubnetPoolsV2,
 | 
				
			||||||
 | 
					                           ApicAimTestCase):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
@@ -35,8 +35,6 @@ from opflexagent import constants as ocst
 | 
				
			|||||||
from oslo_config import cfg
 | 
					from oslo_config import cfg
 | 
				
			||||||
from oslo_serialization import jsonutils
 | 
					from oslo_serialization import jsonutils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
sys.modules["apicapi"] = mock.Mock()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from gbpservice.neutron.plugins.ml2.drivers.grouppolicy.apic import driver
 | 
					from gbpservice.neutron.plugins.ml2.drivers.grouppolicy.apic import driver
 | 
				
			||||||
from gbpservice.neutron.services.grouppolicy import (
 | 
					from gbpservice.neutron.services.grouppolicy import (
 | 
				
			||||||
    group_policy_context as p_context)
 | 
					    group_policy_context as p_context)
 | 
				
			||||||
@@ -97,6 +95,8 @@ class ApicMappingTestCase(
 | 
				
			|||||||
    def setUp(self, sc_plugin=None, nat_enabled=True,
 | 
					    def setUp(self, sc_plugin=None, nat_enabled=True,
 | 
				
			||||||
              pre_existing_l3out=False, default_agent_conf=True,
 | 
					              pre_existing_l3out=False, default_agent_conf=True,
 | 
				
			||||||
              ml2_options=None):
 | 
					              ml2_options=None):
 | 
				
			||||||
 | 
					        self.saved_apicapi = sys.modules["apicapi"]
 | 
				
			||||||
 | 
					        sys.modules["apicapi"] = mock.Mock()
 | 
				
			||||||
        if default_agent_conf:
 | 
					        if default_agent_conf:
 | 
				
			||||||
            self.agent_conf = AGENT_CONF
 | 
					            self.agent_conf = AGENT_CONF
 | 
				
			||||||
        cfg.CONF.register_opts(sg_cfg.security_group_opts, 'SECURITYGROUP')
 | 
					        cfg.CONF.register_opts(sg_cfg.security_group_opts, 'SECURITYGROUP')
 | 
				
			||||||
@@ -240,6 +240,10 @@ class ApicMappingTestCase(
 | 
				
			|||||||
        self.driver.apic_manager.apic.fvCtx.name = echo2
 | 
					        self.driver.apic_manager.apic.fvCtx.name = echo2
 | 
				
			||||||
        self._db_plugin = n_db.NeutronDbPluginV2()
 | 
					        self._db_plugin = n_db.NeutronDbPluginV2()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def tearDown(self):
 | 
				
			||||||
 | 
					        sys.modules["apicapi"] = self.saved_apicapi
 | 
				
			||||||
 | 
					        super(ApicMappingTestCase, self).tearDown()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def _build_external_dict(self, name, cidr_exposed, is_edge_nat=False):
 | 
					    def _build_external_dict(self, name, cidr_exposed, is_edge_nat=False):
 | 
				
			||||||
        ext_info = {
 | 
					        ext_info = {
 | 
				
			||||||
            'enable_nat': 'True' if self.nat_enabled else 'False'
 | 
					            'enable_nat': 'True' if self.nat_enabled else 'False'
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,10 +59,13 @@ gbpservice.neutron.group_policy.policy_drivers =
 | 
				
			|||||||
    nuage_gbp_driver = gbpservice.neutron.services.grouppolicy.drivers.nuage.driver:NuageGBPDriver
 | 
					    nuage_gbp_driver = gbpservice.neutron.services.grouppolicy.drivers.nuage.driver:NuageGBPDriver
 | 
				
			||||||
neutron.ml2.mechanism_drivers =
 | 
					neutron.ml2.mechanism_drivers =
 | 
				
			||||||
    logger_plus = gbpservice.neutron.tests.unit.plugins.ml2plus.drivers.mechanism_logger:LoggerPlusMechanismDriver
 | 
					    logger_plus = gbpservice.neutron.tests.unit.plugins.ml2plus.drivers.mechanism_logger:LoggerPlusMechanismDriver
 | 
				
			||||||
 | 
					    apic_aim = gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.mechanism_driver:ApicMechanismDriver
 | 
				
			||||||
    apic_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.apic.driver:APICMechanismGBPDriver
 | 
					    apic_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.apic.driver:APICMechanismGBPDriver
 | 
				
			||||||
    nuage_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.nuage.driver:NuageMechanismGBPDriver
 | 
					    nuage_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.nuage.driver:NuageMechanismGBPDriver
 | 
				
			||||||
    odl_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.odl.driver:OdlMechanismGBPDriver
 | 
					    odl_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.odl.driver:OdlMechanismGBPDriver
 | 
				
			||||||
    stitching_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.stitching.driver:TrafficStitchingMechanismGBPDriver
 | 
					    stitching_gbp = gbpservice.neutron.plugins.ml2.drivers.grouppolicy.stitching.driver:TrafficStitchingMechanismGBPDriver
 | 
				
			||||||
 | 
					neutron.ml2.extension_drivers =
 | 
				
			||||||
 | 
					    apic_aim = gbpservice.neutron.plugins.ml2plus.drivers.apic_aim.extension_driver:ApicExtensionDriver
 | 
				
			||||||
gbpservice.neutron.servicechain.servicechain_drivers =
 | 
					gbpservice.neutron.servicechain.servicechain_drivers =
 | 
				
			||||||
    dummy = gbpservice.neutron.services.servicechain.plugins.msc.drivers.dummy_driver:NoopDriver
 | 
					    dummy = gbpservice.neutron.services.servicechain.plugins.msc.drivers.dummy_driver:NoopDriver
 | 
				
			||||||
    simplechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.simplechain_driver:SimpleChainDriver
 | 
					    simplechain_driver = gbpservice.neutron.services.servicechain.plugins.msc.drivers.simplechain_driver:SimpleChainDriver
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,6 +27,7 @@ os-testr>=0.4.1 # Apache-2.0
 | 
				
			|||||||
ddt>=1.0.1 # MIT
 | 
					ddt>=1.0.1 # MIT
 | 
				
			||||||
pylint==1.4.5 # GNU GPL v2
 | 
					pylint==1.4.5 # GNU GPL v2
 | 
				
			||||||
reno>=0.1.1 # Apache2
 | 
					reno>=0.1.1 # Apache2
 | 
				
			||||||
 | 
					pyOpenSSL>=0.13.0,<=0.15.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Since version numbers for these are specified in
 | 
					# Since version numbers for these are specified in
 | 
				
			||||||
# https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt,
 | 
					# https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt,
 | 
				
			||||||
@@ -35,3 +36,4 @@ python-heatclient
 | 
				
			|||||||
python-keystoneclient
 | 
					python-keystoneclient
 | 
				
			||||||
 | 
					
 | 
				
			||||||
-e git+https://github.com/noironetworks/python-opflex-agent.git@master#egg=python-opflexagent-agent
 | 
					-e git+https://github.com/noironetworks/python-opflex-agent.git@master#egg=python-opflexagent-agent
 | 
				
			||||||
 | 
					-e git+https://github.com/noironetworks/aci-integration-module.git#egg=aci-integration-module
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user