neutron-dynamic-routing-16.0.0/0000775000175000017500000000000013656750704016436 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/0000775000175000017500000000000013656750703017202 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/0000775000175000017500000000000013656750704020503 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/cli/0000775000175000017500000000000013656750704021252 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/cli/bgp-speaker.rst0000664000175000017500000001525713656750631024215 0ustar zuulzuul00000000000000=========== BGP Speaker =========== BGP Speaker Create ------------------ .. code-block:: console usage: neutron bgp-speaker-create [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [--tenant-id TENANT_ID] --local-as LOCAL_AS [--ip-version {4,6}] [--advertise-floating-ip-host-routes {True,False}] [--advertise-tenant-networks {True,False}] NAME Create a BGP Speaker with a specified NAME. **Positional arguments:** ``NAME`` Name of the BGP speaker to create. **Optional arguments:** ``-h, --help`` show this help message and exit ``--local-as LOCAL_AS`` Local AS number. (Integer in [1, 65535] is allowed.) ``--ip-version {4,6}`` IP version for the BGP speaker (default is 4) ``--advertise-floating-ip-host-routes {True,False}`` Whether to enable or disable the advertisement of floating-ip host routes by the BGP speaker. By default floating ip host routes will be advertised by the BGP speaker. ``--advertise-tenant-networks {True,False}`` Whether to enable or disable the advertisement of tenant network routes by the BGP speaker. By default tenant network routes will be advertised by the BGP speaker. BGP Speaker List ---------------- .. code-block:: console usage: neutron bgp-speaker-list [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] [-P SIZE] [--sort-key FIELD] [--sort-dir {asc,desc}] List BGP speakers. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Speaker Show ---------------- .. code-block:: console usage: neutron bgp-speaker-show [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [-D] [-F FIELD] BGP_SPEAKER Show information of a given BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker to look up. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Speaker Delete ------------------ .. code-block:: console usage: neutron bgp-speaker-delete [-h] [--request-format {json}] BGP_SPEAKER Delete a BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker to delete. **Optional arguments:** ``-h, --help`` show this help message and exit BGP Speaker Update ------------------ .. code-block:: console usage: neutron bgp-speaker-update [-h] [--request-format {json}] [--name NAME] [--advertise-floating-ip-host-routes {True,False}] [--advertise-tenant-networks {True,False}] BGP_SPEAKER Update BGP Speaker's information. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker to update. **Optional arguments:** ``-h, --help`` show this help message and exit ``--name NAME`` Name of the BGP speaker to update. ``--advertise-floating-ip-host-routes {True,False}`` Whether to enable or disable the advertisement of floating-ip host routes by the BGP speaker. By default floating ip host routes will be advertised by the BGP speaker. ``--advertise-tenant-networks {True,False}`` Whether to enable or disable the advertisement of tenant network routes by the BGP speaker. By default tenant network routes will be advertised by the BGP speaker. Add Network to BGP Speaker --------------------------- .. code-block:: console usage: neutron bgp-speaker-network-add [-h] [--request-format {json}] BGP_SPEAKER NETWORK Add a network to the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``NETWORK`` ID or name of the network to add. **Optional arguments:** ``-h, --help`` show this help message and exit Delete Network from BGP Speaker ------------------------------- .. code-block:: console usage: neutron bgp-speaker-network-remove [-h] [--request-format {json}] BGP_SPEAKER NETWORK Remove a network from the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``NETWORK`` ID or name of the network to remove. **Optional arguments:** ``-h, --help`` show this help message and exit BGP Advertised Routes List -------------------------- .. code-block:: console usage: neutron bgp-speaker-advertiseroute-list [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] [-P SIZE] [--sort-key FIELD] [--sort-dir {asc,desc}] BGP_SPEAKER List routes advertised by a given BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. neutron-dynamic-routing-16.0.0/doc/source/cli/dynamic-routing-agent.rst0000664000175000017500000000641013656750631026211 0ustar zuulzuul00000000000000===================== Dynamic Routing Agent ===================== Add BGP Speaker to Dynamic Routing Agent ---------------------------------------- .. code-block:: console usage: neutron bgp-dragent-speaker-add [-h] [--request-format {json}] BGP_DRAGENT_ID BGP_SPEAKER Add a BGP speaker to a Dynamic Routing agent. **Positional arguments:** ``BGP_DRAGENT_ID`` ID of the Dynamic Routing agent. ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit Delete BGP Speaker from Dynamic Routing Agent --------------------------------------------- .. code-block:: console usage: neutron bgp-dragent-speaker-remove [-h] [--request-format {json}] BGP_DRAGENT_ID BGP_SPEAKER Removes a BGP speaker from a Dynamic Routing agent. **Positional arguments:** ``BGP_DRAGENT_ID`` ID of the Dynamic Routing agent. ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit List BGP Speakers hosted by a Dynamic Routing Agent --------------------------------------------------- .. code-block:: console usage: neutron bgp-speaker-list-on-dragent [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] BGP_DRAGENT_ID List BGP speakers hosted by a Dynamic Routing agent. **Positional arguments:** ``BGP_DRAGENT_ID`` ID of the Dynamic Routing agent. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. List Dynamic Routing Agents Hosting a BGP Speaker ------------------------------------------------- .. code-block:: console usage: neutron bgp-dragent-list-hosting-speaker [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] BGP_SPEAKER List Dynamic Routing agents hosting a BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. neutron-dynamic-routing-16.0.0/doc/source/cli/index.rst0000664000175000017500000000255413656750631023120 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Command-Line Interface ====================== Neutron client has provided the command-line interfaces (CLI) to realize dynamic routing services supported by neutron-dynamic-routing project. Current implementation only supports the command line interfaces for BGP functionality. For query on what specific :command:`neutron bgp` commands are supported, enter: .. code-block:: console $ neutron help | grep bgp .. toctree:: :maxdepth: 2 :glob: * neutron-dynamic-routing-16.0.0/doc/source/cli/bgp-peer.rst0000664000175000017500000001063313656750631023507 0ustar zuulzuul00000000000000======== BGP Peer ======== BGP Peer Create --------------- .. code-block:: console usage: neutron bgp-peer-create [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [--tenant-id TENANT_ID] --peer-ip PEER_IP_ADDRESS --remote-as PEER_REMOTE_AS [--auth-type PEER_AUTH_TYPE] [--password AUTH_PASSWORD] NAME Create a BGP Peer. **Positional arguments:** ``NAME`` Name of the BGP peer to create ``--peer-ip PEER_IP_ADDRESS`` Peer IP address. ``--remote-as PEER_REMOTE_AS`` Peer AS number. (Integer in [1, 65535] is allowed.) **Optional arguments:** ``-h, --help`` show this help message and exit ``--auth-type PEER_AUTH_TYPE`` Authentication algorithm. Supported algorithms: none(default), md5 ``--password AUTH_PASSWORD`` Authentication password. BGP Peer List ------------- .. code-block:: console usage: neutron bgp-peer-list [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] [-P SIZE] [--sort-key FIELD] [--sort-dir {asc,desc}] List BGP peers. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Peer Show ------------- .. code-block:: console usage: neutron bgp-peer-show [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [-D] [-F FIELD] BGP_PEER Show information of a given BGP peer. **Positional arguments:** ``BGP_PEER`` ID or name of the BGP peer to look up. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Peer Delete --------------- .. code-block:: console usage: neutron bgp-peer-delete [-h] [--request-format {json}] BGP_PEER Delete a BGP peer. **Positional arguments:** ``BGP_PEER`` ID or name of the BGP peer to delete. **Optional arguments:** ``-h, --help`` show this help message and exit BGP Peer Update --------------- .. code-block:: console usage: neutron bgp-peer-update [-h] [--request-format {json}] [--name NAME] [--password AUTH_PASSWORD] BGP_PEER Update BGP Peer's information. **Positional arguments:** ``BGP_PEER`` ID or name of the BGP peer to update. **Optional arguments:** ``-h, --help`` show this help message and exit ``--name NAME`` Updated name of the BGP peer. ``--password AUTH_PASSWORD`` Updated authentication password. Add Peer to BGP Speaker ----------------------- .. code-block:: console usage: neutron bgp-speaker-peer-add [-h] [--request-format {json}] BGP_SPEAKER BGP_PEER Add a peer to the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``BGP_PEER`` ID or name of the BGP peer to add. **Optional arguments:** ``-h, --help`` show this help message and exit Delete Peer from BGP Speaker ---------------------------- .. code-block:: console usage: neutron bgp-speaker-peer-remove [-h] [--request-format {json}] BGP_SPEAKER BGP_PEER Remove a peer from the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``BGP_PEER`` ID or name of the BGP peer to remove. **Optional arguments:** ``-h, --help`` show this help message and exit neutron-dynamic-routing-16.0.0/doc/source/reference/0000775000175000017500000000000013656750704022441 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/reference/index.rst0000664000175000017500000002717013656750631024310 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) API === Introduction ------------ Neutron dynamic routing project adds the support for dynamic routing protocols in neutron. Using the ReST interface, a cloud administrator can define routing peers and advertise neutron routes outside the OpenStack domain. .. note:: Currently, only the support for BGP dynamic routing protocol is available. Data Model ---------- New data models are defined for supporting routing protocols. Below models are defined for different protocols. BGP ~~~ BGP Speaker +++++++++++ * ``id`` The uuid of BGP Speaker. * ``name`` The name of BGP Speaker. * ``local_as`` The local AS value, ranges from 1 to 65535. * ``ip_version`` The ip address version for BGP Speaker. 4 by default. * ``peers`` The remote peer connection which supports BGP. * ``networks`` The tenant networks connected to the BGP Speaker. * ``advertise_floating_ip_host_routes`` Whether to enable or disable the advertisement of floating ip host routes by the BGP Speaker. True by default. * ``advertise_tenant_networks`` Whether to enable or disable the advertisement of tenant network routes by the BGP Speaker. True by default. BGP Peer ++++++++ * ``id`` The uuid of BGP peer. * ``name`` The name of BGP peer. * ``peer_ip`` The IP address of BGP peer. * ``remote_as`` The remote AS value, ranges from 1 to 65535. * ``auth_type`` The authentication algorithm. Supported algorithms: none and md5, none by default. * ``password`` The authentication password for the specified authentication type. ReST Interface -------------- Different ReST interface are exposed for realizing different dynamic protocol functionality. .. note:: Only an administrator have the access to the exposed API's. BGP ~~~ BGP Speaker +++++++++++ Create '''''' Issue a ``POST`` request to ``/v2.0/bgp-speakers`` with following JSON-encoded data to create a BGP Speaker: :: { "bgp_speaker":{ "ip_version":4, "local_as":"1000", "name":"bgp-speaker" } } Response body: { "bgp_speaker":{ "peers":[ ], "name":"bgp-speaker", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "local_as":1000, "advertise_tenant_networks":true, "networks":[ ], "ip_version":4, "advertise_floating_ip_host_routes":true, "id":"5e08db80-db77-4b5c-a56d-dbca0b284f2c" } } Return code: 201 List '''' Issue a ``GET`` request to ``/v2.0/bgp-speakers`` to retrieve this list of available BGP Speakers. :: Response body: { "bgp_speakers":[ { "peers":[ ], "name":"bgp-speaker-1", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "local_as":1001, "advertise_tenant_networks":true, "networks":[ ], "ip_version":4, "advertise_floating_ip_host_routes":true, "id":"5e08db80-db77-4b5c-a56d-dbca0b284f2c" }, { "peers":[ ], "name":"bgp-speaker", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "local_as":1000, "advertise_tenant_networks":true, "networks":[ ], "ip_version":4, "advertise_floating_ip_host_routes":true, "id":"b759b2a1-27f4-4a6b-bb61-f2c9a22c9902" } ] } Return code: 200 Show '''' Issue a ``GET`` request to ``/v2.0/bgp-speakers/`` to retrieve the detail about a specific BGP Speaker. :: Response body: { "bgp_speaker":{ "peers":[ ], "name":"bgp-speaker", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "local_as":1000, "advertise_tenant_networks":true, "networks":[ ], "ip_version":4, "advertise_floating_ip_host_routes":true, "id":"b759b2a1-27f4-4a6b-bb61-f2c9a22c9902" } } Return code: 200 Update '''''' Issue ``PUT`` request to ``/v2.0/bgp-speakers/`` to update a specific BGP Speaker. Following attributes can be updated. * ``name`` The name of BGP Speaker. * ``advertise_floating_ip_host_routes`` Whether to enable or disable the advertisement of floating ip host routes by the BGP Speaker. True by default. * ``advertise_tenant_networks`` Whether to enable or disable the advertisement of tenant network routes by the BGP Speaker. True by default. Delete '''''' Issue ``DELETE`` request to ``/v2.0/bgp-speakers/`` to delete a specific BGP Speaker. :: No response body Return code: 204 BGP Peer ++++++++ Create '''''' Issue a ``POST`` request to ``/v2.0/bgp-peers`` with following JSON-encoded data to create a BGP peer: :: { "bgp_peer":{ "auth_type":"none", "remote_as":"1001", "name":"bgp-peer", "peer_ip":"10.0.0.3" } } Response body: { "bgp_peer":{ "auth_type":"none", "remote_as":"1001", "name":"bgp-peer", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "peer_ip":"10.0.0.3", "id":"a7193581-a31c-4ea5-8218-b3052758461f" } } Return code: 201 List '''' Issue a ``GET`` request to ``/v2.0/bgp-peers`` to retrieve the list of available BGP peers. :: Response body: { "bgp_peers":[ { "auth_type":"none", "remote_as":1001, "name":"bgp-peer", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "peer_ip":"10.0.0.3", "id":"a7193581-a31c-4ea5-8218-b3052758461f" } ] } Return code: 200 Show '''' Issue a ``GET`` request to ``/v2.0/bgp-peers/`` to retrieve the detail about a specific BGP peer. :: Response body: { "bgp_peer":{ "auth_type":"none", "remote_as":1001, "name":"bgp-peer", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "peer_ip":"10.0.0.3", "id":"a7193581-a31c-4ea5-8218-b3052758461f" } } Return code: 200 Update '''''' Issue ``PUT`` request to ``/v2.0/bgp-peers/`` to update a specific BGP peer. Following attributes can be updated. * ``name`` The name of BGP peer. * ``password`` The authentication password. Delete '''''' Issue ``DELETE`` request to ``/v2.0/bgp-peers/`` to delete a specific BGP peer. :: No response body Return code: 204 BGP Speaker and Peer binding ++++++++++++++++++++++++++++ Add BGP Peer to a BGP Speaker ''''''''''''''''''''''''''''' Issue a ``PUT`` request to ``/v2.0/bgp-speakers//add-bgp-peer`` to bind the BGP peer to the specified BGP Seaker with following JSON-encoded data: :: { "bgp_peer_id":"a7193581-a31c-4ea5-8218-b3052758461f" } Response body: :: { "bgp_peer_id":"a7193581-a31c-4ea5-8218-b3052758461f" } Return code: 200 Remove BGP Peer from a BGP Speaker '''''''''''''''''''''''''''''''''' Issue a ``DELETE`` request with following data to ``/v2.0/bgp-speakers//remove-bgp-peer`` to unbind the BGP peer: :: { "bgp_peer_id":"a7193581-a31c-4ea5-8218-b3052758461f" } No response body Return code: 200 BGP Speaker and Network binding +++++++++++++++++++++++++++++++ Add Network to a BGP Speaker '''''''''''''''''''''''''''' Issue a ``PUT`` request with following data to ``/v2.0/bgp-speakers//add_gateway_network`` to add a network to the specified BGP speaker: :: { "network_id":"f2269b61-6755-4174-8f64-5e318617b204" } Response body: { "network_id":"f2269b61-6755-4174-8f64-5e318617b204" } Return code: 200 Delete Network from a BGP Speaker ''''''''''''''''''''''''''''''''' Issue a ``DELETE`` request with following data to ``/v2.0/bgp-speakers//remove_gateway_network`` to delete a network from a specified BGP speaker. :: No response body Return code: 200 BGP Speaker Advertised Routes +++++++++++++++++++++++++++++ List routes advertised by a BGP Speaker ''''''''''''''''''''''''''''''''''''''' Issue ``GET`` request to ```/v2.0/bgp-speakers//get_advertised_routes`` to list all routes advertised by the specified BGP Speaker. :: Response body: { "advertised_routes":[ { "cidr":"192.168.10.0/24", "nexthop":"10.0.0.1" } ] } Return code: 200 BGP Speaker and Dynamic Routing Agent interaction +++++++++++++++++++++++++++++++++++++++++++++++++ Add BGP Speaker to a Dynamic Routing Agent '''''''''''''''''''''''''''''''''''''''''' Issue a ``POST`` request to ``/v2.0/agents//bgp-drinstances`` to add a BGP Speaker to the specified dynamic routing agent. The following is the request body: :: { "bgp_speaker_id": "5639072c-49eb-480a-9f11-953386589bc8" } No response body Return code: 201 List BGP speakers hosted by a Dynamic Routing Agent ''''''''''''''''''''''''''''''''''''''''''''''''''' Issue a ``GET`` request to ``/v2.0/agents//bgp-drinstances`` to list all BGP Seakers hosted on the specified dynamic routing agent. :: Response body: { "bgp_speakers":[ { "peers":[ ], "name":"bgp-speaker", "tenant_id":"34a6e17a48cf414ebc890367bf42266b", "local_as":1000, "advertise_tenant_networks":true, "networks":[ ], "ip_version":4, "advertise_floating_ip_host_routes":true, "id":"b759b2a1-27f4-4a6b-bb61-f2c9a22c9902" } ] } Return code: 200 List Dynamic Routing Agents hosting a specific BGP Speaker '''''''''''''''''''''''''''''''''''''''''''''''''''''''''' Issue a ``GET`` request to ``/v2.0/bgp-speakers//bgp-dragents`` to list all BGP dynamic agents which are hosting the specified BGP Speaker. :: Response body: { "agents":[ { "binary":"neutron-bgp-dragent", "description":null, "admin_state_up":true, "heartbeat_timestamp":"2016-05-17 03:05:12", "availability_zone":null, "alive":true, "topic":"bgp_dragent", "host":"yangyubj-virtual-machine", "agent_type":"BGP dynamic routing agent", "resource_versions":{ }, "created_at":"2016-05-09 07:38:00", "started_at":"2016-05-11 09:06:13", "id":"af216618-29d3-4ee7-acab-725bdc90e614", "configurations":{ "advertise_routes":0, "bgp_peers":0, "bgp_speakers":1 } } ] } Return code: 200 Delete BGP Speaker from a Dynamic Routing Agent ''''''''''''''''''''''''''''''''''''''''''''''' Issue a ``DELETE`` request to ``/v2.0/agents//bgp-drinstances/`` to delete the BGP Speaker hosted by the specified dynamic routing agent. :: No response body Return code: 204 Reference --------- None neutron-dynamic-routing-16.0.0/doc/source/_static/0000775000175000017500000000000013656750704022131 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/_static/.placeholder0000664000175000017500000000000013656750631024401 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/conf.py0000664000175000017500000001003113656750631021774 0ustar zuulzuul00000000000000# 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 os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', #'sphinx.ext.intersphinx', 'openstackdocstheme', 'oslo_config.sphinxext', 'oslo_config.sphinxconfiggen', 'oslo_policy.sphinxext', 'oslo_policy.sphinxpolicygen', 'sphinxcontrib.rsvgconverter', ] # openstackdocstheme options repository_name = 'openstack/neutron-dynamic-routing' bug_project = 'neutron' bug_tag = 'doc' # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = u'neutron-dynamic-routing' copyright = u'2013, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' html_theme = 'openstackdocs' html_static_path = ['_static'] html_last_updated_fmt = '%Y-%m-%d %H:%M' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # -- Options for LaTeX output ------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual], torctree_only). latex_documents = [ ('index', 'doc-%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual', # Specify toctree_only=True for a better document structure of # the generated PDF file. True), ] # Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 latex_use_xindy = False latex_domain_indices = False latex_elements = { 'makeindex': '', 'printindex': '', 'preamble': r'\setcounter{tocdepth}{3}', } # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'bgp_dragent.ini', ] def _get_config_generator_config_definition(conf): config_file_path = '../../etc/oslo-config-generator/%s' % conf # oslo_config.sphinxconfiggen appends '.conf.sample' to the filename, # strip file extentension (.conf or .ini). output_file_path = '_static/config_samples/%s' % conf.rsplit('.', 1)[0] return (config_file_path, output_file_path) config_generator_config_file = [ _get_config_generator_config_definition(conf) for conf in _config_generator_config_files ] # -- Options for oslo_policy.sphinxpolicygen --------------------------------- policy_generator_config_file = '../../etc/oslo-policy-generator/policy.conf' sample_policy_basename = '_static/neutron-dynamic-routing' neutron-dynamic-routing-16.0.0/doc/source/install/0000775000175000017500000000000013656750704022151 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/install/index.rst0000664000175000017500000000221413656750631024010 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ============ Installation ============ At the command line:: $ pip install neutron-dynamic-routing Or, if you have virtualenv wrapper installed:: $ mkvirtualenv neutron-dynamic-routing $ pip install neutron-dynamic-routing neutron-dynamic-routing-16.0.0/doc/source/index.rst0000664000175000017500000000306113656750631022343 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) =================================================== Welcome to neutron-dynamic-routing's documentation! =================================================== .. NOTE(amotoki): toctree_only=False is specified in latex_documents in doc/source/conf.py to get a better structure of the PDF doc. This means the content of this file (index.rst) is NOT rendered in the generated PDF file. .. include:: ../../README.rst Contents ======== .. toctree:: :maxdepth: 2 install/index admin/index configuration/index reference/index cli/index contributor/index Indices and tables ================== * :ref:`genindex` * :ref:`search` neutron-dynamic-routing-16.0.0/doc/source/contributor/0000775000175000017500000000000013656750704023055 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/contributor/dragent-drivers.rst0000664000175000017500000001136213656750631026711 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) DRAgent Drivers =============== Introduction ------------ The Neutron dynamic routing drivers are used to support different dynamic routing protocol stacks which implement the dynamic routing functionality. As shown in the following figure, the drivers are managed by `DRAgent <./agent-scheduler.html>`_ through a "Driver Manager" which provides consistent APIs to realize the functionality of a dynamic routing protocol:: Neutron Dynamic Routing Drivers +-------------------------------+ | DRAgent | | | | +-------------------------+ | | | Driver Manager | | | +-------------------------+ | | | Common Driver API | | | +-------------------------+ | | | | | | | | +------------+------------+ | | | os-ken | Other | | | | Driver | Drivers | | | +------------+------------+ | | | +-------------------------------+ .. note:: In the first release, only the integration with os-ken is supported. Later release will have support for Quagga, Bird etc. Besides, BGP is the only protocol supported now but support for more dynamic routing protocols might come in the future. Configuration ------------- Driver configurations are done in a separate configuration file. BGP Driver ~~~~~~~~~~ There are two configuration parameters related to BGP which are specified in ``bgp_dragent.ini``. * bgp_speaker_driver, to define BGP speaker driver class. Default is os-ken (neutron_dynamic_routing.services.bgp.agent.driver.os_ken.driver.OsKenBgpDriver). * bgp_router_id, to define BGP identity (typically an IPv4 address). Default is a unique loopback interface IP address. Common Driver API ----------------- Common Driver API is needed to provide a generic and consistent interface to different drivers. Each driver need to implement the provided `base driver class `_. BGP ~~~ Following interfaces need to be implemented by a driver for realizing BGP functionality. +--------------------------------+-----------------------------------------+ |API name |Description | +================================+=========================================+ |add_bgp_speaker() |Add a BGP Speaker | +--------------------------------+-----------------------------------------+ |delete_bgp_speaker() |Delete a BGP speaker | +--------------------------------+-----------------------------------------+ |add_bgp_peer() |Add a BGP peer | +--------------------------------+-----------------------------------------+ |delete_bgp_peer() |Delete a BGP peer | +--------------------------------+-----------------------------------------+ |advertise_route() |Add a new prefix to advertise | +--------------------------------+-----------------------------------------+ |withdraw_route() |Withdraw an advertised prefix | +--------------------------------+-----------------------------------------+ |get_bgp_speaker_statistics() |Collect BGP Speaker statistics | +--------------------------------+-----------------------------------------+ |get_bgp_peer_statistics() |Collect BGP Peer statistics | +--------------------------------+-----------------------------------------+ neutron-dynamic-routing-16.0.0/doc/source/contributor/testing.rst0000664000175000017500000006430513656750631025273 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Testing ======= Dynamic routing enables advertisement of self-service network prefixes to physical network devices that support a dynamic routing protocol, such as routers. The Neutron dynamic routing project consists of a service plugin-in and an agent that can advertise Neutron private network to outside of OpenStack. This document will describe how to test the Dynamic Routing functionalities, introduce what the environment architecture is for dynamic routing test and show how to setup dynamic routing environment using Devstack. Environment Architecture ------------------------- Use the following example architecture as a test environment to deploy neutron-dynamic-routing in your environment. The example architecture will deploy an all-in-one OpenStack and connect to an Ubuntu VM running Quagga as a router outside of OpenStack. See following:: +--------------+ 10.156.18.20 | | +----------------------------------| Quagga | | BGP Peering Session | Router | | | 172.24.4.3 | | +--------------+ | | |10.156.18.21 | External Network(172.24.4.0/24) -------------------------------------------------------------------------------------------------- |eth0 +---------------------------------------+ | | | | | br-ex | | +----------------+ | | | |172.24.4.1 | | +------------+ +-------+ | | | | |Router | | | | Dr-Agent | | | | | | | +-------+ | | +------------+ | | | ---------------- | | Tenant Network | | (10.0.0.0/24) | | | +---------------------------------------+ All-In-One OpenStack Installation Devstack Setup -------------- 1. Download devstack:: git clone https://opendev.org/openstack/devstack.git 2. Enable neutron-dynamic-routing by including this in your local.conf file:: [[local|localrc]] enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing 3. Run devstack:: ./stack.sh Quagga Configure ---------------- Quagga is a network routing software available in most GNU/Linux, Solaris, FreeBSD, and NetBSD. It provides the implementation of OSPF, RIP, BGP and IS-IS. This section shows you how to install Quagga and then configure it on Ubuntu Linux. 1. Install Quagga using apt-get:: $ sudo apt-get install quagga quagga-doc 2. Create an empty file (/etc/quagga/zebra.conf) and set permissions. The Quagga files and configurations will be stored in /etc/quagga:: $ sudo touch /etc/quagga/zebra.conf $ sudo chown quagga.quagga /etc/quagga/zebra.conf $ sudo chmod 640 /etc/quagga/zebra.conf 3. Update quagga daemon file. You can enable/disable the daemons routing in the /etc/quagga/daemons file. Update /etc/quagga/daemons to enable zebra and bgp:: zebra=yes bgpd=yes ospfd=no ospf6d=no ripd=no ripngd=no isisd=no 4. Update /etc/quagga/zebra.conf:: # Zebra configuration # name of the router hostname quagga_1 password zebra # log log file /var/log/quagga/zebra.log 5. Update /etc/quagga/bgpd.conf:: # set router-id to the network address we announce bgp router-id 10.156.18.20 # declare a router with local-as 1000 router bgp 1000 # expose neighbor network which dynamic routing agent is using neighbor 10.156.18.21 remote-as 12345 # treat neutron dynamic routing agent as a passive peer in case # quagga keeps making futile connection attempts neighbor 10.156.18.21 passive # log log file /var/log/quagga/bgpd.log debug bgp events debug bgp filters debug bgp fsm debug bgp keepalives debug bgp updates 6. Restart the Quagga:: $ sudo /etc/init.d/quagga restart Service Test ------------- 1. As the dynamic routing is only supported by admin, source the devstack admin credentials:: $ . devstack/openrc admin admin 2. Verify that the neutron dynamic routing agent is running. .. code-block:: console $ openstack network agent list --agent-type bgp +--------------------+--------------------+--------------------+-------------------+-------+-------+---------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------+--------------------+--------------------+-------------------+-------+-------+---------------------+ | 69ad386f-e055-4284 | BGP dynamic | yang-devstack- | | :-) | UP | neutron-bgp-dragent | | -8c8e-ef9bd540705c | routing agent | ubuntu-1604 | | | | | +--------------------+--------------------+--------------------+-------------------+-------+-------+---------------------+ 3. Create an address scope. The provider(external) and tenant networks must belong to the same address scope for the agent to advertise those tenant network prefixes. .. code-block:: console $ openstack address scope create --ip-version 4 --share public +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | id | c02c358a-9d35-43ea-8313-986b3e4a91c0 | | ip_version | 4 | | name | public | | project_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | shared | True | +------------+--------------------------------------+ 4. Create subnet pools. The provider and tenant networks use different pools. * Create the provider network pool. .. code-block:: console $ neutron subnetpool-create --pool-prefix 172.24.4.0/24 \ --address-scope public provider Created a new subnetpool: +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | 238aaf8f-f91a-4538-b6b2-c0140111cf69 | | created_at | 2016-06-30T07:03:52 | | default_prefixlen | 8 | | default_quota | | | description | | | id | 8439bfee-e09c-40a9-a3ea-8cf7212b7ba9 | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | provider | | prefixes | 172.24.4.0/24 | | shared | False | | tenant_id | 21734c4383284cf9906b7fe8246bffb1 | | updated_at | 2016-06-30T07:03:52 | +-------------------+--------------------------------------+ * Create tenant network pool. .. code-block:: console $ neutron subnetpool-create --pool-prefix 10.0.0.0/16 \ --address-scope public --shared selfservice Created a new subnetpool: +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | c02c358a-9d35-43ea-8313-986b3e4a91c0 | | created_at | 2016-06-30T07:08:30 | | default_prefixlen | 8 | | default_quota | | | description | | | id | c7e9737a-cfd3-45b5-a861-d1cee1135a92 | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | selfservice | | prefixes | 10.0.0.0/16 | | shared | True | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-06-30T07:08:30 | +-------------------+--------------------------------------+ 5. Create the provider and tenant networks. * Create the provider network. .. code-block:: console $ neutron net-create --router:external True --provider:physical_network provider \ --provider:network_type flat provider Created a new network: +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | True | | id | 68ec148c-181f-4656-8334-8f4eb148689d | | name | provider | | provider:network_type | flat | | provider:physical_network | provider | | provider:segmentation_id | | | router:external | True | | shared | False | | status | ACTIVE | | subnets | | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +---------------------------+--------------------------------------+ * Create a subnet on the provider network using an IP address allocation from the provider subnet pool. .. code-block:: console $ neutron subnet-create --name provider --subnetpool provider \ --prefixlen 24 provider Created a new subnet: +-------------------+------------------------------------------------+ | Field | Value | +-------------------+------------------------------------------------+ | allocation_pools | {"start": "172.24.4.2", "end": "172.24.4.254"} | | cidr | 172.24.4.0/24 | | created_at | 2016-03-17T23:17:16 | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 172.24.4.1 | | host_routes | | | id | 8ed65d41-2b2a-4f3a-9f92-45adb266e01a | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | provider | | network_id | 68ec148c-181f-4656-8334-8f4eb148689d | | subnetpool_id | 3771c0e7-7096-46d3-a3bd-699c58e70259 | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-03-17T23:17:16 | +-------------------+------------------------------------------------+ * Create the tenant network. .. code-block:: console $ neutron net-create private Created a new network: +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | True | | id | 01da3e19-129f-4d26-b065-255ade0e5e2c | | name | private | | shared | False | | status | ACTIVE | | subnets | | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +---------------------------+--------------------------------------+ * Create a subnet on the tenant network using an IP address allocation from the private subnet pool. .. code-block:: console $ neutron subnet-create --name selfservice --subnetpool selfservice \ --prefixlen 24 private Created a new subnet: +-------------------+--------------------------------------------+ | Field | Value | +-------------------+--------------------------------------------+ | allocation_pools | {"start": "10.0.0.2", "end": "10.0.0.254"} | | cidr | 10.0.0.0/24 | | created_at | 2016-03-17T23:20:20 | | description | | | dns_nameservers | | | enable_dhcp | True | | gateway_ip | 10.0.0.1 | | host_routes | | | id | 8edd3dc2-df40-4d71-816e-a4586d61c809 | | ip_version | 4 | | ipv6_address_mode | | | ipv6_ra_mode | | | name | private | | network_id | 01da3e19-129f-4d26-b065-255ade0e5e2c | | subnetpool_id | c7e9737a-cfd3-45b5-a861-d1cee1135a92 | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | updated_at | 2016-03-17T23:20:20 | +-------------------+--------------------------------------------+ 6. Create and configure router * Create a router. .. code-block:: console $ neutron router-create router +-----------------------+--------------------------------------+ | Field | Value | +-----------------------+--------------------------------------+ | admin_state_up | True | | external_gateway_info | | | id | 49439b14-f6ee-420d-8c48-d3767fadcb3a | | name | router | | status | ACTIVE | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------+--------------------------------------+ * Add the private subnet as an interface on the router. .. code-block:: console $ neutron router-interface-add router selfservice Added interface 969a1d4b-7fa1-4346-9963-de06becab87a to router router. * Add the provide network as a gateway on the router .. code-block:: console $ neutron router-gateway-set router provider Set gateway for router router * Verify router ports. Note: from this result, you can see what the advertised routes are. .. code-block:: console $ neutron router-port-list router +--------------------------------------+------+-------------------+----------------------------------------------------+ | id | name | mac_address | fixed_ips | +--------------------------------------+------+-------------------+----------------------------------------------------+ | dc675aab-5a8b-462c-872e-2f791b6c1730 | | fa:16:3e:e5:a2:d2 | {"subnet_id": "1c6b725e- | | | | | 890e-4454-8842-7ff22ffa704b", "ip_address": | | | | | "10.0.0.1"} | | e15c701d-868f-4171-a282-e6a4567a8d83 | | fa:16:3e:28:86:4c | {"subnet_id": | | | | | "b442c453-7e4a-4568-9d70-1dde91a65fbb", | | | | | "ip_address": "172.24.4.2"} | +--------------------------------------+------+-------------------+----------------------------------------------------+ 7. Create and configure the BGP speaker The BGP speaker advertised the next-hop IP address for the tenant network prefix. * Create the BGP speaker. Replace LOCAL_AS with an appropriate local autonomous system number. The example configuration uses AS 12345. .. code-block:: console $ neutron bgp-speaker-create --ip-version 4 \ --local-as LOCAL_AS bgp-speaker Created a new bgp_speaker: +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | | ip_version | 4 | | local_as | 12345 | | name | bgp-speaker | | networks | | | peers | | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------------------+--------------------------------------+ * Associate the BGP speaker with the provider network. A BGP speaker requires association with a provider network to determine eligible prefixes. After the association, the BGP speaker can advertise the tenant network prefixes with the corresponding router as the next-hop IP address. .. code-block:: console $ neutron bgp-speaker-network-add bgp-speaker provider Added network provider to BGP speaker bgp-speaker. * Verify the association of the provider network with the BGP speaker. Checking the ``networks`` attribute. .. code-block:: console $ neutron bgp-speaker-show bgp-speaker +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | | ip_version | 4 | | local_as | 12345 | | name | bgp-speaker | | networks | 68ec148c-181f-4656-8334-8f4eb148689d | | peers | | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------------------+--------------------------------------+ * Verify the prefixes and next-hop ip addresses that the BGP speaker advertises. .. code-block:: console $ neutron bgp-speaker-advertiseroute-list bgp-speaker +-------------+------------+ | destination | next_hop | +-------------+------------+ | 10.0.0.0/24 | 172.24.4.3 | +-------------+------------+ * Create a BGP peer. Here the BGP peer is pointed to the quagga VM. Replace REMOTE_AS with an appropriate remote autonomous system number. The example configuration uses AS 12345 which triggers iBGP peering. .. code-block:: console $ neutron bgp-peer-create --peer-ip 10.156.18.20 \ --remote-as REMOTE_AS bgp-peer Created a new bgp_peer: +-----------+--------------------------------------+ | Field | Value | +-----------+--------------------------------------+ | auth_type | none | | id | 35c89ca0-ac5a-4298-a815-0b073c2362e9 | | name | bgp-peer | | peer_ip | 10.156.18.20 | | remote_as | 12345 | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------+--------------------------------------+ * Add a BGP peer to the BGP speaker. .. code-block:: console $ neutron bgp-speaker-peer-add bgp-speaker bgp-peer Added BGP peer bgppeer to BGP speaker bgpspeaker. * Verify the association of the BGP peer with the BGP speaker. Checking the ``peers`` attribute. .. code-block:: console $ neutron bgp-speaker-show bgp-speaker +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 5f227f14-4f46-4eca-9524-fc5a1eabc358 | | ip_version | 4 | | local_as | 12345 | | name | bgp-speaker | | networks | 68ec148c-181f-4656-8334-8f4eb148689d | | peers | 35c89ca0-ac5a-4298-a815-0b073c2362e9 | | tenant_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | +-----------------------------------+--------------------------------------+ 8. Schedule the BGP speaker to an agent. * Schedule the BGP speaker to ``BGP dynamic routing agent`` The first BGP speaker is scheduled to the first dynamic routing agent automatically. So for a simple setup, there is nothing to be done here. * Verify scheduling of the BGP speaker to the agent. .. code-block:: console neutron bgp-dragent-list-hosting-speaker bgp-speaker +--------------------------------------+---------------------------+----------------+-------+ | id | host | admin_state_up | alive | +--------------------------------------+---------------------------+----------------+-------+ | 69ad386f-e055-4284-8c8e-ef9bd540705c | yang-devstack-ubuntu-1604 | True | :-) | +--------------------------------------+---------------------------+----------------+-------+ neutron-dynamic-routing-16.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000175313656750631026323 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ============ Contributing ============ .. include:: ../../../CONTRIBUTING.rst neutron-dynamic-routing-16.0.0/doc/source/contributor/index.rst0000664000175000017500000000306713656750631024723 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) =============== Developer Guide =============== In the Developer Guide, you will find information on neutron-dynamic-routing lower level programming APIs. There are sections that cover the core pieces of neutron-dynamic-routing, including its API, command-lines, database, system-design, alembic-migration etc. There are also subsections that describe specific drivers inside neutron-dynamic-routing. Finally, the developer guide includes information about testing and supported functionalities as well. This documentation is generated by the Sphinx toolkit and lives in the source tree. .. toctree:: :maxdepth: 2 contributing testing dragent-drivers neutron-dynamic-routing-16.0.0/doc/source/admin/0000775000175000017500000000000013656750704021573 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/admin/bgp-speaker.rst0000664000175000017500000001673513656750631024540 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) BGP Speaker =========== BGP Speaker acts as a route server using BGP routing protocol. It advertises routes to the BGP peers which are added to the BGP Speaker. Now there is a framework that allows different `BGP drivers <../contributor/dragent-drivers.html>`_ to be plugged into a `dynamic routing agent <./agent-scheduler.html>`_. Currently, BGP Speaker only advertises routes for a network to which it is associated. A BGP Speaker requires association with a "gateway" network to determine eligible routes. In Neutron, a "gateway" network connects Neutron routers to the upstream routers. An external network is best for being used as a gateway network. The association builds a list of all virtual routers with gateways on provider and self-service networks within the same address scope. Hence, the BGP speaker advertises self-service network prefixes with the corresponding router as the next-hop IP address. For details refer to `Route advertisement <./route-advertisement.html>`_. Address Scopes -------------- `Address scopes `_ provide flexible control as well as decoupling of address overlap from tenancy, so this kind control can provide a routable domain, the domain has itself route and no overlap address, it means an address scope define "a L3 routing domain". BGP Speaker will associate the external networks and advertise the tenant's networks routes. Those networks should reside in the same address scope. Neutron can route the tenant network directly without NAT. Then Neutron can host globally routable IPv4 and IPv6 tenant networks. For determining which tenant networks prefixes should be advertised, Neutron will identify all routers with gateway ports on the network which had been bounded with BGP Speaker, check the address scope of the subnets on all connected networks, then begin advertising nexthops for all tenant networks to routers on the bound network. BGP Peer -------- BGP peer defined in Neutron represents real BGP infrastructure such as routers, route reflectors and route servers. When a BGP peer is defined and associated with a BGP Speaker, Neutron will attempt to open a BGP peering session with the mentioned remote peer. It is this session, using which Neutron announces it's routes. How to configure a remote peer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A remote peer can be real or virtual e.g. vRouters or real routers. The remote peer should be configured to handle peering with Neutron in passive mode. The peer needs to waits for the Neutron dynamic routing agent to initiate the peering session. Also, the remote peer can be configured in active mode, but it still can speak BGP until the complete initialization of BGP Speaker running on Neutron dynamic routing agent. Configuring BGP Speaker: One needs to ensure below points for setting a BGP connection. * Host running Neutron dynamic agent MUST connect to the external router. * BGP configuration on the router should be proper. ``bgp router-id XX.XX.XX.XX`` This must be an IP address, the unique identifier of BGP routers actually and can be virtual. If one doesn't configure the router-id, it will be selected automatically as the highest IP address configured for the local interfaces. Just a suggestion, please make sure that it is the same as the ``peer_ip`` which you configure in Neutron for distinguishing easily. ``local_as`` Autonomous System number can be same or different from the AS_id of external BGP router. AS_id will be same for iBGP and different for eBGP sessions. Setting BGP peer: :: neighbor A.B.C.D remote-as AS_ID A.B.C.D is the host IP which run Neutron dynamic routing agent. A Sample Quagga router configuration file forming BGP peering with Neutron: :: ! password zebra log file /var/log/quagga/bgpd.log ! debug bgp events debug bgp keepalives debug bgp updates debug bgp fsm debug bgp filters ! bgp multiple-instance ! router bgp view test-as bgp router-id neighbor remote-as neighbor passive ! line vty ! BGP Speaker Architecture ------------------------ Dynamic routing project saves BGP Speaker configuration as per the defined `data model `_. and pass on the configuration request to the dynamic routing agent for further processing. The implementation of a BGP Speaker is driver specific. During the driver interface initialization process, needed configurations are read from the configuration file and BGP Speaker object instance is created. For details refer to `BGP drivers <../contributor/dragent-drivers.html>`_. BGP Speaker Life Cycle ~~~~~~~~~~~~~~~~~~~~~~ Now we support OsKenBgpDriver, BGP Speaker will be processed by Dragent. When associating a BGP Speaker with an active Dragent, the plugin will send an RPC message to the agent for calling driver in order to create a BGP Speaker instance. In OsKenBgpDriver, the created instance ``BGP Speaker`` will setup by router-id and ASN, then os-ken will setup new context with speaker configuration and listeners which monitor whether the related peers are alive. Then the following operation could be done. * Add peers to BGP Speaker When BGP Speaker is not associated with an active Dragent, there is no real speaker instance, so it will be still the db operation until the speaker is associated with dragent, and all the peers connection before will be setup by ``BGP Speaker`` creation. If add peers into speaker which is running, Dragent will call driver to add peer dynamically. For OsKenBgpDriver, it will register a new neighbor based on your peer configuration and try to establish a session with the peer. * Delete peers from BGP Speaker The same logic with below, but it is reverse. If you don't want use the specific BGP Speaker anymore, you can use CLI: ``neutron bgp-speaker-delete `` BGP Plugin will find all the associated Dragent and send RPC ``bgp_speaker_remove_end`` to make the Dragents to clean the ``BGP Speaker`` instances. This is the same with CLI: ``neutron bgp-dragent-speaker-remove `` BGP Plugin just send rpc ``bgp_speaker_remove_end`` to the specific Dragent. Advertisement ~~~~~~~~~~~~~ For details refer to `Route Advertisement <./route-advertisement.html>`_. How to work ----------- For details refer to `Testing <../contributor/testing.html>`_. neutron-dynamic-routing-16.0.0/doc/source/admin/system-design.rst0000664000175000017500000001330613656750631025122 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) System Design ============= Introduction ------------ Neutron dynamic routing enables advertisement of self-service (private) network prefixes to physical network devices that support dynamic routing protocols such as routers, thus removing the conventional dependency on static routes. It advertises three classes of routes: * Host routes for floating IP addresses hosted on non-DVR routers, the nexthop is the centralized router. * Host routes for floating IP addresses hosted on DVR routers, the nexthop is the appropriate compute node. * Prefix routes for directly routable tenant networks with address scopes, the nexthop is the centralized router, the same for DVR and CVR. For details refer to `Route Advertisement <./route-advertisement.html>`_. Neutron dynamic routing consists of `service plug-in `_ and agent. The service plug-in implements the Networking service extension and the agent manages dynamic routing protocol peering sessions. The plug-in communicates with the agent through RPC. Architecture ------------ The following figure shows the architecture of this feature:: Neutron dynamic Routing System Architecture +---------------------------------------------------------------+ | Dynamic Routing plug-in | | +---------------------------------------------------------+ | | | Dynamic Routing API/Model | | | +---------------------------------------------------------+ | | | Dynamic Routing Agent Scheduler | | | +---------------------------------------------------------+ | | | | +------------------------------|--------------------------------+ | | +-----------+ | RPC | +-----------+ | | +----------------------|-------------------------+ | | | | +---------------------------+ +---------------------------+ | Dynamic Routing Agent1 | | Dynamic Routing Agent2 | | | | | | +---------------------+ | | +---------------------+ | | | Driver Manager | | | | Driver Manager | | | +---------------------+ | | +---------------------+ | | | Common Driver API | | | | Common Driver API | | | +---------------------+ | | +---------------------+ | | | | | | | | +---------+-----------+ | | +---------+-----------+ | | | os-ken | Other | | | | os-ken | Other | | | | Driver | Drivers | | | | Driver | Drivers | | | +---------+-----------+ | | +---------+-----------+ | | | | | +---------------------------+ +---------------------------+ Dynamic Routing Plug-in ~~~~~~~~~~~~~~~~~~~~~~~ Using dynamic routing plugin one can enable/disable the support of dynamic routing protocols in neutron. Dynamic Routing API ~~~~~~~~~~~~~~~~~~~ Dynamic routing API provides APIs to configure dynamic routing. API's for below mentioned dynamic protocols are supported. BGP +++ Three kinds of APIs are available for BGP functionality.For details refer to the `API document <../reference/index.html>`_. * BGP Speaker APIs to advertise Neutron routes outside the Openstack network. * BGP Peer APIs to form peers with the remote routers. * BGP DRAgentScheduler APIs to schedule BGP Speaker(s) to one or more hosts running the dynamic routing agent. .. note:: BGP is the only dynamic routing protocol currently supported. Dynamic Routing Model ~~~~~~~~~~~~~~~~~~~~~ Dynamic routing model maintains the database and communicates with the dynamic routing agent. Dynamic Routing Agent Scheduler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dynamic routing agent scheduler, is responsible for scheduling a routing entity. For details refer to `Agent Scheduler <./agent-scheduler.html>`_. Dynamic Routing Agent (DR Agent) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dynamic routing can reside on hosts with or without other Networking service agents. It manages and configures different dynamic routing stack through `Common Driver API <../contributor/dragent-drivers.html>`_. .. note:: Currently, only integration with `os-ken `_ is supported. Future releases will add the support for Quagga, Bird, etc. neutron-dynamic-routing-16.0.0/doc/source/admin/route-advertisement.rst0000664000175000017500000001262313656750631026336 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Route Advertisement =================== BGP --- This page discusses the behavior of BGP dynamic routing about how to advertise routes and show the routes details in the project. BGP dynamic routing could advertise 3 classes of routes: * Host routes for floating IP addresses hosted on non-DVR routers, as floatingip address set on the router namespace, it knows how to route the message to the correct way, so the next-hop should be the IP address of router gateway port. * Host routes for floating IP addresses hosted on DVR routers. With DVR-enabled routers, the floating IP can be reached directly on the compute node hosting a given instance. As such, host routes for the floating IP address should advertise the FIP agent gateway on the compute node as the next-hop instead of the centralized router. This will keep inbound floating IP traffic from encountering the bottleneck of the centralized router. * Prefix routes for directly routable tenant networks with address scopes, the nexthop is the centralized router, the same for DVR and CVR. BGP dynamic routing could advertise tenant network prefixes to physical network devices(routers which support BGP protocol), called this ``Prefixes advertisement``. When distributed virtual routing (DVR) is enabled on a router, next-hops for floating IP's and fixed IP's are not advertised as being at the centralized router. Host routes with the next-hop set to the appropriate compute node are advertised. Logical Model ~~~~~~~~~~~~~ :: +--------+ 1 N +---------------------+ | Router |---------| BgpAdvertisedRoute | +--------+ +---------------------+ | N | | 1 +---------+ N N +------------+ N N +---------+ | BgpPeer |-----------| BgpSpeaker |-----------| Network | +---------+ +------------+ +---------+ | N | | 1 +--------------+ | AddressScope | +--------------+ .. note:: A BGP Speaker only supports one address family to speak BGP. A dual-stack IPv4 and IPv6 network needs two BGP Speakers to advertise the routes with BGP, one for IPv4 and the other for IPv6. So A network can have N number of BGP Speakers bound to it. BgpAdvertisedRoute represents derived data. As the number of BgpAdvertisedRoutes can be quite large, storing in a database table is not feasible. BgpAdvertisedRoute information can be derived by joining data already available in the Neutron database. And now BGP dynamic routing project process the Bgpadvertiseroutes which should be advertised to external Router is basing on the exist Neutron DB tables. Neutron looks on each of the gateway network for any routers with a gateway port on that network. For each router identified, Neutron locates each floating IP and tenant network accessible through the router gateway port. Neutron then advertises each floating IP and tenant network with the IP address of the router gateway port as the next hop. When BGP Plugin is started, it will register callbacks. All callbacks are used for processing Floating IP, Router Interface and Router Gateway creation or update, this functions listen the events of these resources for calling Dragent to change the advertisement routes. Now we just focus on the resources which may cause route change, the following callbacks does this work. * floatingip_update_callback This function listens to the Floating IP's AFTER_UPDATE event, it judges whether the associated router is changed, and changes the advertisement routes and nexthop based on that. * router_interface_callback This function listens to the tenants' network routes change, it listens to AFTER_CREATE and AFTER_DELETE events of Router Interface resource. It calls Dragent to advertise or stop the prefix routes after a interface attach into a router. * router_gateway_callback This function listens to the router gateway port creation or deletion. It also focuses on tenants' network routes change. You could get the advertisement routes of specific BGP Speaker like: ``neutron bgp-speaker-advertiseroute-list `` It does a complicated db query to generate the list of advertised routes. For more details refer to `route advertisement db lookup `_ neutron-dynamic-routing-16.0.0/doc/source/admin/index.rst0000664000175000017500000000211213656750631023427 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ==================== Administration Guide ==================== .. toctree:: :maxdepth: 2 system-design bgp-speaker route-advertisement agent-scheduler neutron-dynamic-routing-16.0.0/doc/source/admin/agent-scheduler.rst0000664000175000017500000001117713656750631025405 0ustar zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. 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. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ===== Agent ===== Neutron-dynamic-routing implements a new agent named "DRAgent". The agent talks to the neutron-dynamic-routing plugin which resides in the neutron server to get routing entity configuration. DRAgent interacts with the back-end driver to realize the required dynamic routing protocol functionality. For details, please refer to the system design document :doc:`system-design` .. note:: One DRAgent can support multiple drivers but currently ONLY os-ken is integrated successfully. Scheduler ========= Neutron-dynamic-routing scheduler, schedules a routing entity to a proper DRAgent. BGP Scheduler ------------- BGP Speaker and DRAgent has 1:N association which means one BGP speaker can be scheduled on multiple DRAgents. Here is an example to associate/disassociate a BGP Speaker to/from a DRAgent. :: (neutron) bgp-speaker-list +--------------------------------------+------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------+----------+------------+ | 0967eb04-59e5-4ca6-a0b0-d584d8d4a132 | bgp2 | 200 | 4 | | a73432c3-a3fc-4b1e-9be2-6c32a61df579 | bgp1 | 100 | 4 | +--------------------------------------+------+----------+------------+ (neutron) agent-list +--------------------------------------+---------------------------+---------------------+-------------------+-------+----------------+---------------------------+ | id | agent_type | host | availability_zone | alive | admin_state_up | binary | +--------------------------------------+---------------------------+---------------------+-------------------+-------+----------------+---------------------------+ | 0c21a829-4fd6-4375-8e65-36db4dc434ac | DHCP agent | steve-devstack-test | nova | :-) | True | neutron-dhcp-agent | | 0f9d6886-910d-4af4-b248-673b22eb9e78 | Metadata agent | steve-devstack-test | | :-) | True | neutron-metadata-agent | | 5908a304-b9d9-4e8c-a0af-96a066a7c87e | Open vSwitch agent | steve-devstack-test | | :-) | True | neutron-openvswitch-agent | | ae74e375-6a75-4ebe-b85c-6628d2baf02f | L3 agent | steve-devstack-test | nova | :-) | True | neutron-l3-agent | | dbd9900e-9d16-444d-afc4-8d0035df5ed5 | BGP dynamic routing agent | steve-devstack-test | | :-) | True | neutron-bgp-dragent | +--------------------------------------+---------------------------+---------------------+-------------------+-------+----------------+---------------------------+ (neutron) bgp-dragent-speaker-add dbd9900e-9d16-444d-afc4-8d0035df5ed5 bgp1 Associated BGP speaker bgp1 to the Dynamic Routing agent. (neutron) bgp-speaker-list-on-dragent dbd9900e-9d16-444d-afc4-8d0035df5ed5 +--------------------------------------+------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------+----------+------------+ | a73432c3-a3fc-4b1e-9be2-6c32a61df579 | bgp1 | 100 | 4 | +--------------------------------------+------+----------+------------+ (neutron) bgp-dragent-speaker-remove dbd9900e-9d16-444d-afc4-8d0035df5ed5 bgp1 Disassociated BGP speaker bgp1 from the Dynamic Routing agent. (neutron) bgp-speaker-list-on-dragent dbd9900e-9d16-444d-afc4-8d0035df5ed5 (neutron) .. note:: Currently, auto-scheduling is not supported. ReST API's for neutron-dynamic-routing scheduler is defined in the API document :doc:`/reference/index` neutron-dynamic-routing-16.0.0/doc/source/configuration/0000775000175000017500000000000013656750704023352 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/configuration/policy-sample.rst0000664000175000017500000000117413656750631026664 0ustar zuulzuul00000000000000========================================== Sample neutron-dynamic-routing Policy File ========================================== The following is a sample neutron-dynamic-routing policy file for adaptation and use. The sample policy can also be viewed in :download:`file form `. .. important:: The sample policy file is auto-generated from neutron-dynamic-routing when this documentation is built. You must ensure your version of neutron-dynamic-routing matches the version of this documentation. .. literalinclude:: /_static/neutron-dynamic-routing.policy.yaml.sample neutron-dynamic-routing-16.0.0/doc/source/configuration/policy.rst0000664000175000017500000000052213656750631025401 0ustar zuulzuul00000000000000================================ neutron-dynamic-routing policies ================================ The following is an overview of all available policies in neutron-dynamic-routing. For a sample configuration file, refer to :doc:`/configuration/policy-sample`. .. show-policy:: :config-file: etc/oslo-policy-generator/policy.conf neutron-dynamic-routing-16.0.0/doc/source/configuration/samples/0000775000175000017500000000000013656750704025016 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/doc/source/configuration/samples/bgp_dragent.rst0000664000175000017500000000041213656750631030020 0ustar zuulzuul00000000000000====================== Sample bgp_dragent.ini ====================== This sample configuration can also be viewed in `the raw format <../../_static/config_samples/bgp_dragent.conf.sample>`_. .. literalinclude:: ../../_static/config_samples/bgp_dragent.conf.sample neutron-dynamic-routing-16.0.0/doc/source/configuration/index.rst0000664000175000017500000000142113656750631025210 0ustar zuulzuul00000000000000=================== Configuration Guide =================== Configuration ------------- This section provides a list of all possible options for each configuration file. neutron-dynamic-routing uses the following configuration files for its various services. .. toctree:: :maxdepth: 1 bgp_dragent The following are sample configuration files for neutron-dynamic-routing. These are generated from code and reflect the current state of code in the neutron-dynamic-routing repository. .. toctree:: :glob: :maxdepth: 1 samples/* Policy ------ neutron-dynamic-routing, like most OpenStack projects, uses a policy language to restrict permissions on REST API actions. .. toctree:: :maxdepth: 1 Policy Reference Sample Policy File neutron-dynamic-routing-16.0.0/doc/source/configuration/bgp_dragent.rst0000664000175000017500000000017613656750631026363 0ustar zuulzuul00000000000000=============== bgp_dragent.ini =============== .. show-options:: :config-file: etc/oslo-config-generator/bgp_dragent.ini neutron-dynamic-routing-16.0.0/requirements.txt0000664000175000017500000000202413656750631021717 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT httplib2>=0.9.1 # MIT netaddr>=0.7.18 # BSD SQLAlchemy>=1.2.0 # MIT alembic>=0.8.10 # MIT six>=1.10.0 # MIT neutron-lib>=1.26.0 # Apache-2.0 os-ken>=0.3.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.db>=4.27.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 neutron>=14.0.0.0b1 # Apache-2.0 # The comment below indicates this project repo is current with neutron-lib # and should receive neutron-lib consumption patches as they are released # in neutron-lib. It also implies the project will stay current with TC # and infra initiatives ensuring consumption patches can land. # neutron-lib-current neutron-dynamic-routing-16.0.0/setup.cfg0000664000175000017500000000351713656750704020265 0ustar zuulzuul00000000000000[metadata] name = neutron-dynamic-routing summary = Neutron Dynamic Routing description-file = README.rst author = OpenStack author-email = openstack-discuss@lists.openstack.org home-page = https://docs.openstack.org/neutron-dynamic-routing/latest/ python-requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 [files] packages = neutron_dynamic_routing [entry_points] console_scripts = neutron-bgp-dragent = neutron_dynamic_routing.cmd.eventlet.agents.bgp_dragent:main neutron.db.alembic_migrations = neutron-dynamic-routing = neutron_dynamic_routing.db.migration:alembic_migrations oslo.config.opts = bgp.agent = neutron_dynamic_routing.services.bgp.common.opts:list_bgp_agent_opts oslo.policy.policies = neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules neutron.policies = neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules neutron.service_plugins = bgp = neutron_dynamic_routing.services.bgp.bgp_plugin:BgpPlugin [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = neutron_dynamic_routing/locale/neutron_dynamic_routing.pot [compile_catalog] directory = neutron_dynamic_routing/locale domain = neutron_dynamic_routing [update_catalog] domain = neutron_dynamic_routing output_dir = neutron_dynamic_routing/locale input_file = neutron_dynamic_routing/locale/neutron_dynamic_routing.pot [egg_info] tag_build = tag_date = 0 neutron-dynamic-routing-16.0.0/tox.ini0000664000175000017500000001062013656750631017747 0ustar zuulzuul00000000000000[tox] envlist = py37,pep8,pylint minversion = 3.1.1 skipsdist = True [testenv] setenv = VIRTUAL_ENV={envdir} OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true} OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true} OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true} PYTHONWARNINGS=default::DeprecationWarning usedevelop = True install_command =pip install {opts} {packages} deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/ussuri} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt whitelist_externals = find sh commands = find . -type f -name "*.py[c|o]" -delete find . -type l -name "*.py[c|o]" -delete find . -depth -path "*/__pycache__*" -delete stestr run {posargs} # there is also secret magic in stestr which lets you run in a fail only # mode. To do this define the TRACE_FAILONLY environmental variable. [testenv:functional] setenv = OS_TEST_PATH=./neutron_dynamic_routing/tests/functional commands = stestr run {posargs} stestr slowest [testenv:dsvm-functional] setenv = OS_TEST_PATH=./neutron_dynamic_routing/tests/functional OS_SUDO_TESTING=1 OS_ROOTWRAP_CMD=sudo {envdir}/bin/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf OS_ROOTWRAP_DAEMON_CMD=sudo {envdir}/bin/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf OS_FAIL_ON_MISSING_DEPS=1 whitelist_externals = sh cp sudo commands = stestr run {posargs} [testenv:releasenotes] commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pep8] commands = flake8 neutron-db-manage --subproject neutron-dynamic-routing --database-connection sqlite:// check_migration {[testenv:genconfig]commands} {[testenv:genpolicy]commands} [testenv:cover] setenv = {[testenv]setenv} PYTHON=coverage run --source neutron_dynamic_routing --parallel-mode commands = stestr run --no-subunit-trace {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:venv] commands = {posargs} [testenv:docs] commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] envdir = {toxworkdir}/docs # deps = {[testenv:docs]deps} whitelist_externals = make commands = sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [flake8] # E125 continuation line does not distinguish itself from next logical line # E126 continuation line over-indented for hanging indent # E128 continuation line under-indented for visual indent # E129 visually indented line with same indent as next logical line # E265 block comment should start with ‘# ‘ # H405 multi line docstring summary not separated with an empty line # TODO(marun) H404 multi line docstring should start with a summary # N530 direct neutron imports not allowed # TODO(ihrachys) -- reenable N537 when new neutron-lib release is available # N537 Log messages should not be translated # H106 Don’t put vim configuration in source files # H203 Use assertIs(Not)None to check for None # W504 line break after binary operator # (W503 and W504 are incompatible and we need to choose one of them. # Existing codes follows W503, so we disable W504.) ignore = E125,E126,E128,E129,E265,H404,H405,N530,N537,W504 enable-extensions=H106,H203 show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,.ropeproject,rally-scenarios import-order-style = pep8 [testenv:pylint] deps = {[testenv]deps} pylint commands = pylint --rcfile=.pylintrc --output-format=colorized {posargs:neutron_dynamic_routing} [hacking] import_exceptions = neutron_dynamic_routing._i18n local-check-factory = neutron_lib.hacking.checks.factory [testenv:genconfig] commands = {toxinidir}/tools/generate_config_file_samples.sh [testenv:genpolicy] commands = oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt [testenv:dev] # run locally (not in the gate) using editable mode # https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs commands = pip install -q -e "git+https://opendev.org/openstack/neutron#egg=neutron" [testenv:py-dev] commands = {[testenv:dev]commands} {[testenv]commands} [testenv:pep8-dev] deps = {[testenv]deps} commands = {[testenv:dev]commands} {[testenv:pep8]commands} neutron-dynamic-routing-16.0.0/ChangeLog0000664000175000017500000006571413656750703020224 0ustar zuulzuul00000000000000CHANGES ======= 16.0.0 ------ * Monkey patch original current\_thread \_active * Update TOX\_CONSTRAINTS\_FILE for stable/ussuri * Update .gitreview for stable/ussuri 16.0.0.0rc1 ----------- * Fix docs publishing * Cleanup py27 support 16.0.0.0b1 ---------- * Switch functional job to Zuulv3 syntax * Drop python 2 support and testing * Remove tempest path from setup.cfg & tox.ini * Remove tempest tests that have moved to neutron-tempest-plugin * Fix resource\_filter.bind method that was changed in neutron * Switch to Ussuri jobs * Update master for stable/train 15.0.0 ------ * PDF documentation build * use payloads for ROUTER\_GATEWAY events * Add Python 3 Train unit tests * bgp: Gracefully handle missing last\_known\_router\_id * Convert CI jobs to python 3 * Replace git.openstack.org URLs with opendev.org URLs * Dropping the py35 testing * Bump neutron version dependency * Make scenario jobs voting * Updated from global requirements * Bump neutron-lib to 1.26.0 * Update hacking version * OpenDev Migration Patch * stop using common\_db\_mixin * Replace openstack.org git:// URLs with https:// * Update master for stable/stein * Ignore DrAgentAssociationError in test\_remove\_add\_speaker\_agent 14.0.0 ------ * Fix installation of docker when docker-engine is not available 14.0.0.0b2 ---------- * Implement DVR-aware fixed IP lookups * Enable MP-BGP capabilities in Ryu BGP driver * add python 3.7 unit test job 14.0.0.0b1 ---------- * stop using common db mixin methods * doc: Add policy reference * Fix gate issues on master * Add agent\_updated to BgpDrAgentNotifyApi notifier * Convert policy.json into policy-in-code * Migrate neutron-dynamic-routing from Ryu to os-ken * neutron-lib version bump to 1.21.0 * use common rpc and exceptions from neutron-lib * use openstack-lower-constraints-jobs-neutron job template * Change openstack-dev to openstack-discuss * load neutron objects using neutron-lib * add local tox targets for pep8 and py3 * use neutron-lib for \_model\_query * use context manager from neutron-lib * Update min tox version to 2.0 * removing older python version 3.4 from setup.cfg * Remove unnecessary mocking of get\_admin\_context * fix tox python3 overrides * Fix \_get\_config\_generator\_config\_definition() typo * Increment versioning with pbr instruction * Use templates for cover and lower-constraints * opt in for neutron-lib consumption patches * add python 3.6 unit test job * Fix unit tests with py3.6 * switch documentation job to new PTI * import zuul job settings from project-config * Update reno for stable/rocky 13.0.0 ------ * update requirements for neutron-lib 1.18.0 * Remove setting of DEVSTACK\_GATE\_EXERCISES * Add release notes link in README * Improve patching of sys.argv 13.0.0.0b3 ---------- * Switch to stestr 13.0.0.0b2 ---------- * Disable subnet CIDR reservation 13.0.0.0b1 ---------- * Fix requirements * use rpc Connection rather than create\_connection * Fix pep8 issues with import order * Enable mutable config in Neutron-dynamic-routing * remove unused plugin.get\_plugin\_name() * Move 4byte\_asn scenario test to basic dir * Skip functional and scenario tests on doc changes * Drop non-existent verbose option * Updated from global requirements * add lower-constraints job * Avoid tox-install.sh * Updated from global requirements * Fix some minor issues in testing doc * Fix TypeError for BgpSpeakerRescheduleError * use common agent topics from neutron-lib * Update docs * Fix failure when adding a speaker to an agent * Zuul: Remove project name * Update reno for stable/queens * Fix unit tests for floating IP creation * Tag the alembic migration revisions for Queens * Fix broken unit tests 12.0.0.0rc1 ----------- * Updated from global requirements * Adding neutron.service\_plugins for "entry\_points" in file "setup.cfg" * The description of the range for local\_as and remote\_as is incorrect in file "base.py" * Switch to neutron-tempest-plugin for tempest tests 12.0.0.0b3 ---------- * Updated from global requirements * Remove redundant gate config in scenario tests * Updated from global requirements * Address breakage from upstream change * Migrate legacy jobs into neutron-dynamic-routing repo 12.0.0.0b2 ---------- * Updated from global requirements * use l3 api def from neutron-lib * Update after agent extension rehome * Update the doc link * Remove setting of version/release from releasenotes * Updated from global requirements * Use agent OVO for bgp\_dragent\_scheduler * Support 4-Byte AS Numbers * Use FAULT\_MAP from neutron-lib * Updated from global requirements * use external net api def from lib * Fix unmatched argument 12.0.0.0b1 ---------- * Use common constants in neutron-lib * Switch to tempest.common.utils.is\_extension\_enabled * Updated from global requirements * Updated from global requirements * Fix checking extension in api test * Stop using is\_agent\_down * Fix unit tests and test configuration * Give docker bridges test-specific names * Reschedule a bgp speaker binded to a down dr-agent * Fix to use "." to source script files * Updated from global requirements * Update reno for stable/pike 11.0.0.0rc1 ----------- * tempest: Avoid using dvr extension * Updated from global requirements 11.0.0.0b3 ---------- * use synchronized decorator from neutron-lib * Add auto-generated config reference * Tempest: Fix DeprecationWarning for tempest.test.attr() * Update the documentation link for doc migration * Enable some off-by-default checks * The parameter "fields" is lack in call to self.\_make\_bgp\_peer\_dict * Updated from global requirements * Use "docker network" in scenario test * rearrange content to fit the new standard layout * Use flake8-import-order plugin * switch from oslosphinx to openstackdocstheme * Enable warning-is-error in doc build * use service type constants from neutron\_lib plugins * Updated from global requirements * devstack: Adapt to lib/neutron * DB Unit tests: BGP gateway netwrok creation made in a designated method * Updated from global requirements * Use Neutron new engine-facade 11.0.0.0b2 ---------- * Updated from global requirements * Allow BGP DB test to run with non ml2 core plugins * Fix exception message for DuplicateBgpPeerIpException * Updated from global requirements * consume neutron-lib callbacks * Switched remaining jobs to ostestr * Remove subunit-trace fork * Enable BGP extension in gate\_hook.sh * Add sudo permission to tempest user in senario test * Revert "Enable BGP extension" * Enable BGP extension * Fix tempest api tests path for tox.ini * Add tempest scenario tests * Disable new N537 hacking check from next neutron-lib * Updated from global requirements 11.0.0.0b1 ---------- * Updated from global requirements * Enable neutron-dynamic-routing scheduler * Delete non-existent file in index.rst * Optimize the link address for fetching git code * Agent common config move * Use neutron-lib's context module * consume ServicePluginBase from neutron-lib * Updated from global requirements * Fix relocated DB models * Updated from global requirements * Use test l3 plugin from neutron * Update reno for stable/ocata 10.0.0 ------ * Fix broken doc links * Switch to decorators.idempotent\_id * Renamed [BGP] config section to [bgp] * tempest plugin: Don't use the same name as neutron 10.0.0.0b3 ---------- * Updated from global requirements * Use neutron-lib portbindings api-def * Updated from global requirements * Don't override min\_l3\_agents\_per\_router in tests * Enable gate\_hook for tempest api test * Gracefully withdraw the floating IP bgp route * Removes unnecessary utf-8 encoding * Updated from global requirements * Replaces uuid.uuid4 with uuidutils.generate\_uuid() 10.0.0.0b2 ---------- * Fix ipv6 transport failure caused by Ryu 4.9 and above * Updated from global requirements * Allow to run multiple kind of tempest tests * Let the bgp\_plugin to query floating IP bgp next\_hop * Use ExtensionDescriptor from neutron-lib * Use DB field sizes instead of \_MAX\_LEN constants * Remove PLURALS * Show team and repo badges on README * Switch to using neutron-lib's model\_base 10.0.0.0b1 ---------- * Updated from global requirements * Switch to using plugins directory in lieu of neutron manager * Fix no attribute error for 'convert\_to\_boolean' * Updated from global requirements * Remove last vestiges of oslo-incubator * fix the comments * Add bits to run neutron-lib periodic test successfully * Updated from global requirements * fix description, tenant to project * fix the comments error * Removing Alembic migration devref document * Updated from global requirements * Replace 'MagicMock' with 'Mock' * Changed the home-page link * Update module path of RPC classes * Drop use of neutron's eventlet utility function * Updated from global requirements * Enable release notes translation * Updated from global requirements * pep8: switched to neutron-lib hacking checks * Update reno for stable/newton * Stop using \_create\_or\_update\_agent * Updated from global requirements 9.0.0.0rc1 ---------- * Tag the alembic migration revisions for Newton * Fixes KeyError while updating bgp peer * Use relocated address scope import * Use model\_base from neutron-lib 9.0.0.0b3 --------- * Updated from global requirements * Update tox.ini for upper constraints * Revert "Fix NoSuchOptError on identity config option lookup" * Fix NoSuchOptError on identity config option lookup * Fixed "tox -e py27" warning message * Add Python 3.5 venv and classifier * Updated from global requirements * Remove temporary local HasProject * Allow to run functional tests * Enable DeprecationWarning in test environments * Updated from global requirements * BGP: exclude legacy fip in DVR fip host routes query * Remove duplicate test l3plugin * Rename DB columns: tenant -> project * Use six.text\_type instead of unicode * Add bgp speaker and route advertisement doc * Clean up API tests for neutron-dynamic-routing * Fix bug for Permission denied * Updated from global requirements 9.0.0.0b2 --------- * Remove unhelpful test of oslo.service ServiceLauncher * remove unused LOG * Document about how to test dynamic routing * Fix unicode bug for password-authenticated BGP peer * Fix \_get\_id\_for * Fix subunit trace help * Address FutureWarning for invalid UUIDs * Fix API document * Trivial documentation change to trigger publish job * Remove check\_i18n files * Fix typo in the installation documentation * Fix exception translation * BGP dynamic routing CLI * Add drivers document for neutron dynamic routing * Add system design for neutron dynamic routing * Fix the policy check for BGP APIs * Fix test-requirements.txt to pull correct version of hacking * Add neutron-dynamic-routing DrAgent & Scheduler document * BGP dynamic routing api documentation 9.0.0.0b1 --------- * Add "neutron-bgp-dragent" to setup.cfg * Fix bgp-speaker-network-remove error * Add devstack for neutron-dynamic-routing stadium project * Fix the issue about BGP dragent reports state failed * Move BGP service plugin, agent, and tests out of Neutron repo * Added OSLO config framework * Documentation * Added alembic DB migration framework * [Trivial] Remove executable privilege of doc/source/conf.py * Prepare initial sandbox for neutron-dynamic-routing * Generated new .gitreview file for neutron-dynamic-routing * API tests: Check extensions with proper functions * Hacking rule to check i18n usage * Make network segment table available for standalone plugin * DSCP QoS rule implementation * register the config generator default hook with the right name * Using LOG.warning replace LOG.warn * Copy tempest.common.tempest\_fixtures in to Neutron * Queries for DVR-aware floating IP next-hop lookups * Add BGP Callback and agent RPC notifcation implementations * Fix tempest lib import in API tests * Add a description field to all standard resources * Add timestamp for neutron core resources * Skip racey BGP tests * Continue the fwaas decoupling and cleanup * Add Queries For BGP Route Lookups * Add tag mechanism for network resources * Moved CORS middleware configuration into oslo-config-generator * BGP: remove unnecessary configuration setting * Add support for QoS for LinuxBridge agent * Add API extension for reporting IP availability usage statistics * macvtap: Macvtap L2 Agent * Switch to using in-tree tempest lib * BGP Dynamic Routing: introduce BgpDriver * API test for get-me-network * BGP Dynamic Routing: introduce BgpDrAgent * macvtap: ML2 mech driver for macvtap network attachments * Open vSwitch conntrack based firewall driver * Make DHCP agent scheduler physical\_network aware * Add the rebinding chance in \_bind\_port\_if\_needed * Remove deprecation warnings * BGP Dynamic Routing: introduce BgpDrScheduler model * Add BGP Dynamic Routing DB Model and Basic CRUD * Remove vpnaas tests from the Neutron API tree * Add the ability to load a set of service plugins on startup * Implement 'get-me-a-network' API building block * Test helpers to facilitate testing BGP dynamic routing * Postpone heavy policy check for ports to later * Delete Tempest fork, import from tempest and tempest\_lib * Add dns\_db to models/head.py * Remove obsolete plugin stuff * External DNS driver reference implementation * Make neutron pecan server an option instead of binary * LBaaS tests code removal * devstack: don't enable qos service with the plugin * Keep py3.X compatibility for urllib * Enable Guru Meditation Reports for other refarch agents * Refactor the subnetpools API tests * Decompose OFAgent mechanism driver from neutron tree completely * move usage\_audit to cmd/eventlet package * Set timetable for removal of oslo.messaging.notify.drivers * Final decomposition of the nuage plugin * Final decomposition of Brocade vendor code * Remove Neutron core static example configuration files - addition * Hyper-V: remove driver from the neutron tree * Remove version from setup.cfg * Automatically generate neutron core configuration files * Freescale ML2 driver code complete decomposition * Remove BigSwitch plugin and driver * Make sure we return unicode strings for process output * Fix Neutron flavor framework * Remove deprecated use\_namespaces option * Remove SysV init script for neutron-server * Final decomposition of opendaylight driver * Remove MidonetInterfaceDriver * Fix notification driver package * Replace subnetpool config options with admin-only API * Decompose ML2 mechanism driver for Mellanox * Decompose ML2 mechanism driver for OVSvApp * Remove IBM SDN-VE left-overs * Add stevedore aliases for interface\_driver configuration * Remove the embrane plugin * Python 3: add classifiers * Remove OneConvergence plugin from the source tree * Decomposition phase2 for MidoNet plugin * Add availability\_zone support base * Add neutron-linuxbridge-cleanup util * Remove OpenContrail plugin from the source tree * Open Mitaka development * Refactoring devstack script * Introduce a separate RPC server * Revert "Revert "Pecan WSGI: prevent plugins from opening AMQP connections"" * Revert "Pecan WSGI: prevent plugins from opening AMQP connections" * Install sriov-agent.ini on 'setup.py install' * SR-IOV: devstack support for SR-IOV agent * Stop device\_owner from being set to 'network:\*' * Final decomposition of Cisco plugin * Remove Cisco Meta and N1KV monolithic plugins * Remove implicit registration of \*-aas service providers * Add Geneve type driver support to ML2 * Removing the SDN-VE monolithic plugin * PLUMgrid plugin decomposition part II * L3 agent changes and reference implementation for IPv6 PD * Decomposition phase2 of NEC plugin * Add empty policy rule to get\_rule\_type action * Remove the ML2 Nuage driver code * Neutron RBAC API and network support * Added initial devstack plugin * Final decomposition of ML2 Cisco UCSM driver * Fix tenant access to qos policies * Final decomposition of ML2 Nexus Driver * NSX: Move DB models as part of core vendor decomposition * Pecan WSGI: prevent plugins from opening AMQP connections * Remove vmware plugin from neutron (etc part) * Support for independent alembic branches in sub-projects * Remove bigswitch mech\_driver entry point definition * Treat sphinx warnings as errors * Final decomposition of the ML2 NCS driver * SR-IOV: Add Agent QoS driver to support bandwidth limit * Replace 'import json' with oslo\_serialization * Fix get\_objects to allow filtering * Moved extensions/qos\_agent.py into extensions/qos.py * Add API tests for non-accessible policies * Cleaned up some TODO comments for feature/qos that do not apply anymore * Added missing [qos] section into neutron.conf * Fix accessing shared policies, add assoc tests * Initial pecan structure * Arista Drivers decomposition part II * Moved out cisco n1kv mech driver and db models * Load the QoS notification driver from the configuration file * Add update tests for policies and rules * Introduce mechanism to determine supported qos rule types for a plugin * Add DB support for resource usage tracking * QoS: Remove type attribute from QoS rules * Decompose Apic ML2 mechanism driver * Base infrastructure for QoS API tests * Metaplugin removal * Remove deprecated OVS and LB plugin DB tables * Handle qos\_policy on network/port create/update * Add address\_scope\_db to neutron/models/head.py * Flavor Framework implementation * Network RBAC DB setup and legacy migration * Add qos section to ovs agent config * Moving out cisco n1kv extensions * Qos Agent Extension * Python3: do not use urllib.urlencode * AgentExtensionsManager and AgentCoreResourceExtension * Support Basic Address Scope CRUD as extensions * QoS: db models and migration rules * Fix Consolidate sriov agent and driver code * QoS service plugin stub * Create the QoS API extension stub * Consolidate sriov agent and driver code * Restructure agent code in preparation for decomp * Updated from global requirements * Add IPset cleanup script * Update version for Liberty * Decompose the NCS ML2 Mechanism Driver * Implement IPAM Driver loader * Fix minor errors in the Vyatta L3 Plugin: * IPAM reference driver * Centralized register\_OVS\_agent in tests * Update build hooks * mlnx MD: mlnx\_direct removal * Extenuate register\_dhcp\_agent code duplication in tests * Add missed actions into policy.json * Disembowel register\_l3\_agent code duplication in tests * ARP spoofing patch: Low level ebtables integration * Block allowed address pairs on other tenants' net * Removed ml2\_conf\_odl.ini config file * Removed ml2\_conf\_odl.ini config file * Open Liberty development * Reorganize unit test tree * Add API tests for subnetpool allocation * tests: don't rely on configuration files outside tests directory * Test to verify shared attribute of network * Add full-stack tests framework * Add L3 router plugin shim for Brocade MLX * Cisco UCS Manager ML2 Mechanism Driver * Cisco Nexus1000V ML2 Mechanism Driver * Ml2 Mechanism Driver for OVSvApp Solution * Add eventlet monkey\_patch helper * Hyper-V Agent decomposition * Basic subnetpool CRUD * Replace keepalived notifier bash script with Python ip monitor * Add ML2 VLAN mechanism driver for Brocade MLX and ICX switches * IBM SDN-VE Plugin decomposition * Brocade Vyatta vrouter shim plugin for vendor decomposition * Add portsecurity extension support * Move mlnx agent to be under ml2/drivers/mlnx * Update api tests from tempest * Remove vendor entry point * Brocade vendor code decomposition from neutron repo * ML2 cisco\_nexus MD: sync config and models with vendor repo * Add rootwrap daemon mode support * NEC plugin code split * ofagent: Have a thin driver module * Initial copy of api tests from tempest * Vendor decomposition to move CSR1000v support to the networking-cisco repo * Decompose the VMware plugin * Remove HyperVNeutronPlugin * ml2 extension driver: more tests, fix data argument inconsistency * ofagent: Vendor code decomposition * Missing entry points for cisco apic topology agents * Fix retrieval of shared firewall\_policies * Added a policy for retrieving the agent hosting a load balancer * test\_l2population: Use a fake mech driver instead of ofagent * Moved several services into neutron.cmd.eventlet * Move NCS mech driver to its new home * Added policy for lbaas v2 agent extension resource * NEC: Merge NEC plugin models into single module * VMware: consolidate NSX models * oslo: migrate to namespace-less import paths * Drop bw compact module for OpenDayLight * Drop deprecated namespace for oslo.rootwrap * Allow port mac\_address to be modified * Drop bin/neutron-rootwrap * Freescale FWaaS Plugin: Update to setup.cfg * Move agent cleanup scripts to cmd module * Service split: cleaned up setup.cfg * Move metadata agent entry to its own file * Break out config and entry point out of l3/agent file * Move postcommit ops out of transaction for bulk * VMWare-NSXv: VMWare NSXv database models * Delete the console scripts for lbaas and vpnaas * Allow to specify IP address of floating ip * Allow setting a tenant router's external IP * Remove NSX 'service' plugin * Backward compatibility for advanced services * Remove mlnx plugin * Move classes out of l3\_agent.py * Services split, pass 2 * Split services code out of Neutron, pass 1 * Remove Python 2.6 classifier * Remove ryu plugin * Add rootwrap filters for ofagent * Remove openvswitch core plugin entry point * Add advsvc role to neutron policy file * remove linuxbridge plugin * Open Kilo development * Fix entrypoint of OneConvergencePlugin plugin * Separate Configuration from Freescale SDN ML2 mechanism Driver * Remove the Cisco Nexus monolithic plugin * Adds ipset support for Security Groups * Add L3 VRRP HA base classes * Supply missing cisco\_cfg\_agent.ini file * Revert "Cisco DFA ML2 Mechanism Driver" * Big Switch: Separate L3 functions into L3 service * Remove reference to cisco\_cfg\_agent.ini from setup.cfg again * Adds router service plugin for CSR1kv * Support for extensions in ML2 * Cisco DFA ML2 Mechanism Driver * Adding mechanism driver in ML2 plugin for Nuage Networks * Remove ovs dependency in embrane plugin * Remove old policies from policy.json * Fix policy rules for adding and removing router interfaces * Fix bigswitch setup.cfg lines * Opencontrail plug-in implementation for core resources * Remove reference to cisco\_cfg\_agent.ini from setup.cfg * Add L3 Scheduler Changes for Distributed Routers * Configuration agent for Cisco devices * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 2 * This patch changes the name of directory from mech\_arista to arista * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 1 * Add rule for updating network's router:external attribute * L2 Model additions to support DVR * Add L3 Extension for Distributed Routers * Freeze models for healing migration * ofagent: move main module from ryu repository * Port to oslo.messaging * Added missing core\_plugins symbolic names * Freescale SDN Mechanism Driver for ML2 Plugin * Remove run-time version checking for openvswitch features * Added missing plugin .ini files to setup.cfg * Disallow regular user to update firewall's shared attribute * Cisco APIC ML2 mechanism driver, part 2 * NSX: get rid of the last Nicira/NVP bits * Perform policy checks only once on list responses * Remove last parts of Quantum compatibility shim * Open Juno development * One Convergence Neutron Plugin l3 ext support * One Convergence Neutron Plugin Implementation * BigSwitch: Add SSL Certificate Validation * Add OpenDaylight ML2 MechanismDriver * Implementaion of Mechanism driver for Brocade VDX cluster of switches * Implement Mellanox ML2 MechanismDriver * Implement OpenFlow Agent mechanism driver * Finish off rebranding of the Nicira NVP plugin * BigSwitch: Add agent to support neutron sec groups * Adds the new IBM SDN-VE plugin * Replace binding:capabilities with binding:vif\_details * Rename Neutron core/service plugins for VMware NSX * Copy cache package from oslo-incubator * Add support to request vnic type on port * Add migration support from agent to NSX dhcp/metadata services * LBaaS: move agent based driver files into a separate dir * Prepare for multiple cisco ML2 mech drivers * Support building wheels (PEP-427) * Use oslo.rootwrap library instead of local copy * Enables BigSwitch/Restproxy ML2 VLAN driver * Base ML2 bulk support on the loaded drivers * Disallow non-admin users update net's shared attribute * Configure plugins by name * Rename nicira configuration elements to match new naming structure * Rename check\_nvp\_config utility tool * Add fwaas\_driver.ini to setup.cfg * Add vpnaas and debug filters to setup.cfg * Open Icehouse development * Allow non-admin user to list service providers * Allow sharing of firewall rules and policies in policy.json * Add l2 population base classes * Install metering\_agent.ini and vpn\_agent.ini * ML2 Mechanism Driver for Cisco Nexus * Reference driver implementation (IPsec) for VPNaaS * Implement ML2 port binding * Arista ML2 Mechanism driver * ML2 Mechanism Driver for Tail-f Network Control System (NCS) * Add Neutron l3 metering agent * Disallow non-admin to specify binding:profile * Add multiple provider network extension * remove binaries under bin * Add metering extension and base class * Remove DHCP lease logic * Add support for the Nexus 1000V into the Cisco Plugin * Firewall as a Service (FWaaS) APIs and DB Model * Service Type Framework refactoring * remove "get\_agents" rule in policy.json * Add agent scheduling for LBaaS namespace agent * Enable policy control over external\_gateway\_info sub-attributes * Add gre tunneling support for the ML2 plugin * Add VXLAN tunneling support for the ML2 plugin * xenapi - rename quantum to neutron * Initial Modular L2 Mechanism Driver implementation * Rename Quantum to Neutron * Remove single-version-externally-managed in setup.cfg * Fix single-version-externally-mananged typo in setup.cfg * Rename agent\_loadbalancer directory to loadbalancer * Add API mac learning extension for NVP * Rename README to README.rst * Add L3 resources to policy.json * Initial Modular L2 plugin implementation * Reduce plugin accesses from policy engine * Move to pbr * Remove calls to policy.check from plugin logic * add db to save host for port * Remove calls to policy.enforce from plugin and db logic * Make the 'admin' role configurable * Enable authZ checks for member actions * Fix typo in policy.json and checks in nicira plugin * Add scheduling feature basing on agent management extension * Use testtools instead of unittest or unittest2 * Agent management extension * Add nvp qos extension * Adds port security api extension and base class * Use babel to generate translation file * API extension and DB support for service types * Add VIF binding extensions * Update policies * Update default policy for add/remove router interface to admin\_or\_owner * Added policy checks for add interface and remove interface * Policies for external networks * Make sure floating IPs + gateways must be on external nets * remove policy check for host\_routes in update\_port * Enable users to list subnets on shared networks * Adds the 'public network' concept to Quantum * Initial V2 implementation of provider extension * Add authZ through incorporation of policy checks * Fix up test running to match jenkins expectation * Add build\_sphinx options * Split out quantum.client and quantum.common * Getting ready for the client split * Pushing initial started code based on Glance project and infrstructure work done by the melange team neutron-dynamic-routing-16.0.0/.mailmap0000664000175000017500000000013113656750631020051 0ustar zuulzuul00000000000000# Format is: # # neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/0000775000175000017500000000000013656750704025075 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/entry_points.txt0000664000175000017500000000112113656750703030365 0ustar zuulzuul00000000000000[console_scripts] neutron-bgp-dragent = neutron_dynamic_routing.cmd.eventlet.agents.bgp_dragent:main [neutron.db.alembic_migrations] neutron-dynamic-routing = neutron_dynamic_routing.db.migration:alembic_migrations [neutron.policies] neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules [neutron.service_plugins] bgp = neutron_dynamic_routing.services.bgp.bgp_plugin:BgpPlugin [oslo.config.opts] bgp.agent = neutron_dynamic_routing.services.bgp.common.opts:list_bgp_agent_opts [oslo.policy.policies] neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/dependency_links.txt0000664000175000017500000000000113656750703031142 0ustar zuulzuul00000000000000 neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/requires.txt0000664000175000017500000000053213656750703027474 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 eventlet!=0.18.3,!=0.20.1,>=0.18.2 httplib2>=0.9.1 netaddr>=0.7.18 SQLAlchemy>=1.2.0 alembic>=0.8.10 six>=1.10.0 neutron-lib>=1.26.0 os-ken>=0.3.0 oslo.config>=5.2.0 oslo.db>=4.27.0 oslo.log>=3.36.0 oslo.messaging>=5.29.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.service!=1.28.1,>=1.24.0 oslo.utils>=3.33.0 neutron>=14.0.0.0b1 neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/pbr.json0000664000175000017500000000005613656750703026553 0ustar zuulzuul00000000000000{"git_version": "31ea570", "is_release": true}neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/PKG-INFO0000664000175000017500000000374013656750703026175 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: neutron-dynamic-routing Version: 16.0.0 Summary: Neutron Dynamic Routing Home-page: https://docs.openstack.org/neutron-dynamic-routing/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/neutron-dynamic-routing.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on This package contains neutron-dynamic-routing code which depends upon neutron and it's related libraries to run. Project Resources ================= The homepage for Neutron is: https://launchpad.net/neutron. Use this site for asking for help, and filing bugs. We use a single launchpad page for all Neutron projects. Code is available on opendev.org at: https://opendev.org/openstack/neutron-dynamic-routing Refer to Neutron documentation for more information: `Neutron README.rst `_ Release notes for the project can be found at: https://docs.openstack.org/releasenotes/neutron-dynamic-routing/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/top_level.txt0000664000175000017500000000003013656750703027617 0ustar zuulzuul00000000000000neutron_dynamic_routing neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/not-zip-safe0000664000175000017500000000000113656750703027322 0ustar zuulzuul00000000000000 neutron-dynamic-routing-16.0.0/neutron_dynamic_routing.egg-info/SOURCES.txt0000664000175000017500000002046013656750703026762 0ustar zuulzuul00000000000000.coveragerc .mailmap .pylintrc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst TESTING.rst babel.cfg lower-constraints.txt requirements.txt setup.cfg setup.py test-requirements.txt tox.ini devstack/README.rst devstack/plugin.sh devstack/settings devstack/lib/dr doc/source/conf.py doc/source/index.rst doc/source/_static/.placeholder doc/source/admin/agent-scheduler.rst doc/source/admin/bgp-speaker.rst doc/source/admin/index.rst doc/source/admin/route-advertisement.rst doc/source/admin/system-design.rst doc/source/cli/bgp-peer.rst doc/source/cli/bgp-speaker.rst doc/source/cli/dynamic-routing-agent.rst doc/source/cli/index.rst doc/source/configuration/bgp_dragent.rst doc/source/configuration/index.rst doc/source/configuration/policy-sample.rst doc/source/configuration/policy.rst doc/source/configuration/samples/bgp_dragent.rst doc/source/contributor/contributing.rst doc/source/contributor/dragent-drivers.rst doc/source/contributor/index.rst doc/source/contributor/testing.rst doc/source/install/index.rst doc/source/reference/index.rst etc/README.txt etc/oslo-config-generator/bgp_dragent.ini etc/oslo-policy-generator/policy.conf neutron_dynamic_routing/__init__.py neutron_dynamic_routing/_i18n.py neutron_dynamic_routing/version.py neutron_dynamic_routing.egg-info/PKG-INFO neutron_dynamic_routing.egg-info/SOURCES.txt neutron_dynamic_routing.egg-info/dependency_links.txt neutron_dynamic_routing.egg-info/entry_points.txt neutron_dynamic_routing.egg-info/not-zip-safe neutron_dynamic_routing.egg-info/pbr.json neutron_dynamic_routing.egg-info/requires.txt neutron_dynamic_routing.egg-info/top_level.txt neutron_dynamic_routing/api/__init__.py neutron_dynamic_routing/api/rpc/__init__.py neutron_dynamic_routing/api/rpc/agentnotifiers/__init__.py neutron_dynamic_routing/api/rpc/agentnotifiers/bgp_dr_rpc_agent_api.py neutron_dynamic_routing/api/rpc/callbacks/__init__.py neutron_dynamic_routing/api/rpc/callbacks/resources.py neutron_dynamic_routing/api/rpc/handlers/__init__.py neutron_dynamic_routing/api/rpc/handlers/bgp_speaker_rpc.py neutron_dynamic_routing/cmd/__init__.py neutron_dynamic_routing/cmd/eventlet/__init__.py neutron_dynamic_routing/cmd/eventlet/agents/__init__.py neutron_dynamic_routing/cmd/eventlet/agents/bgp_dragent.py neutron_dynamic_routing/db/__init__.py neutron_dynamic_routing/db/bgp_db.py neutron_dynamic_routing/db/bgp_dragentscheduler_db.py neutron_dynamic_routing/db/migration/README neutron_dynamic_routing/db/migration/__init__.py neutron_dynamic_routing/db/migration/alembic_migrations/__init__.py neutron_dynamic_routing/db/migration/alembic_migrations/env.py neutron_dynamic_routing/db/migration/alembic_migrations/script.py.mako neutron_dynamic_routing/db/migration/alembic_migrations/versions/CONTRACT_HEAD neutron_dynamic_routing/db/migration/alembic_migrations/versions/EXPAND_HEAD neutron_dynamic_routing/db/migration/alembic_migrations/versions/start_neutron_dynamic_routing.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/4cf8bc3edb66_rename_tenant_to_project.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/61cc795e43e8_initial.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/expand/f399fa0f5f25_initial.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/contract/a589fdb5724c_change_size_of_as_number.py neutron_dynamic_routing/db/migration/models/__init__.py neutron_dynamic_routing/db/migration/models/head.py neutron_dynamic_routing/extensions/__init__.py neutron_dynamic_routing/extensions/bgp.py neutron_dynamic_routing/extensions/bgp_4byte_asn.py neutron_dynamic_routing/extensions/bgp_dragentscheduler.py neutron_dynamic_routing/policies/__init__.py neutron_dynamic_routing/policies/base.py neutron_dynamic_routing/policies/bgp_dragent.py neutron_dynamic_routing/policies/bgp_peer.py neutron_dynamic_routing/policies/bgp_speaker.py neutron_dynamic_routing/services/__init__.py neutron_dynamic_routing/services/bgp/__init__.py neutron_dynamic_routing/services/bgp/bgp_plugin.py neutron_dynamic_routing/services/bgp/agent/__init__.py neutron_dynamic_routing/services/bgp/agent/bgp_dragent.py neutron_dynamic_routing/services/bgp/agent/config.py neutron_dynamic_routing/services/bgp/agent/entry.py neutron_dynamic_routing/services/bgp/agent/driver/__init__.py neutron_dynamic_routing/services/bgp/agent/driver/base.py neutron_dynamic_routing/services/bgp/agent/driver/exceptions.py neutron_dynamic_routing/services/bgp/agent/driver/utils.py neutron_dynamic_routing/services/bgp/agent/driver/os_ken/__init__.py neutron_dynamic_routing/services/bgp/agent/driver/os_ken/driver.py neutron_dynamic_routing/services/bgp/common/__init__.py neutron_dynamic_routing/services/bgp/common/constants.py neutron_dynamic_routing/services/bgp/common/opts.py neutron_dynamic_routing/services/bgp/scheduler/__init__.py neutron_dynamic_routing/services/bgp/scheduler/bgp_dragent_scheduler.py neutron_dynamic_routing/tests/__init__.py neutron_dynamic_routing/tests/common/__init__.py neutron_dynamic_routing/tests/common/helpers.py neutron_dynamic_routing/tests/contrib/README neutron_dynamic_routing/tests/contrib/gate_hook.sh neutron_dynamic_routing/tests/functional/__init__.py neutron_dynamic_routing/tests/functional/services/__init__.py neutron_dynamic_routing/tests/functional/services/bgp/__init__.py neutron_dynamic_routing/tests/functional/services/bgp/scheduler/__init__.py neutron_dynamic_routing/tests/functional/services/bgp/scheduler/test_bgp_dragent_scheduler.py neutron_dynamic_routing/tests/unit/__init__.py neutron_dynamic_routing/tests/unit/api/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/test_bgp_dr_rpc_agent_api.py neutron_dynamic_routing/tests/unit/api/rpc/handlers/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/handlers/test_bgp_speaker_rpc.py neutron_dynamic_routing/tests/unit/db/__init__.py neutron_dynamic_routing/tests/unit/db/test_bgp_db.py neutron_dynamic_routing/tests/unit/db/test_bgp_dragentscheduler_db.py neutron_dynamic_routing/tests/unit/services/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/test_bgp_plugin.py neutron_dynamic_routing/tests/unit/services/bgp/agent/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/agent/test_bgp_dragent.py neutron_dynamic_routing/tests/unit/services/bgp/driver/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/driver/test_utils.py neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/test_driver.py neutron_dynamic_routing/tests/unit/services/bgp/scheduler/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/scheduler/test_bgp_dragent_scheduler.py playbooks/neutron-dynamic-routing-dsvm-tempest-api/post.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-api/run.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/post.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/run.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv4/post.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv4/run.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv6/post.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv6/run.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4/post.yaml playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4/run.yaml releasenotes/notes/.placeholder releasenotes/notes/drop-py27-support-795303ca12cccd34.yaml releasenotes/notes/dvr-aware-announcements-24bfcb8fee87161d.yaml releasenotes/notes/mp-bgp-support-d408e8569e94d07f.yaml releasenotes/notes/support-4byte-asn-d89d7100c0890ebf.yaml releasenotes/source/README.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tools/check_unit_test_structure.sh tools/clean.sh tools/generate_config_file_samples.shneutron-dynamic-routing-16.0.0/MANIFEST.in0000664000175000017500000000020213656750631020165 0ustar zuulzuul00000000000000include AUTHORS include README.rst include ChangeLog include LICENSE exclude .gitignore exclude .gitreview global-exclude *.pyc neutron-dynamic-routing-16.0.0/etc/0000775000175000017500000000000013656750704017211 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/etc/README.txt0000664000175000017500000000077113656750631020713 0ustar zuulzuul00000000000000To generate the sample neutron-dynamic-routing configuration files and the sample policy file, run the following commands respectively from the top level of the neutron-dynamic-routing directory: tox -e genconfig tox -e genpolicy If a 'tox' environment is unavailable, then you can run the following commands instead to generate the configuration files and the policy file: ./tools/generate_config_file_samples.sh oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf neutron-dynamic-routing-16.0.0/etc/oslo-config-generator/0000775000175000017500000000000013656750704023414 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/etc/oslo-config-generator/bgp_dragent.ini0000664000175000017500000000013213656750631026364 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/bgp_dragent.ini.sample wrap_width = 79 namespace = bgp.agent neutron-dynamic-routing-16.0.0/etc/oslo-policy-generator/0000775000175000017500000000000013656750704023446 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/etc/oslo-policy-generator/policy.conf0000664000175000017500000000012313656750631025607 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/policy.yaml.sample namespace = neutron-dynamic-routing neutron-dynamic-routing-16.0.0/HACKING.rst0000664000175000017500000000042113656750631020230 0ustar zuulzuul00000000000000Neutron Dynamic Routing Style Commandments ========================================== Please see the Neutron HACKING.rst file for style commandments for neutron-dynamic-routing: `Neutron HACKING.rst `_ neutron-dynamic-routing-16.0.0/TESTING.rst0000664000175000017500000000054513656750631020310 0ustar zuulzuul00000000000000Testing Neutron Dynamic Routing =============================== Please see the TESTING.rst file for the Neutron project itself. This will have the latest up to date instructions for how to test Neutron, and will be applicable to neutron-dynamic-routing as well: `Neutron TESTING.rst `_ neutron-dynamic-routing-16.0.0/.pylintrc0000664000175000017500000000664213656750631020312 0ustar zuulzuul00000000000000# The format of this file isn't really documented; just use --generate-rcfile [MASTER] # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. # ignore=.git,tests [MESSAGES CONTROL] # NOTE(gus): This is a long list. A number of these are important and # should be re-enabled once the offending code is fixed (or marked # with a local disable) disable= # "F" Fatal errors that prevent further processing import-error, # "I" Informational noise locally-disabled, # "E" Error for important programming issues (likely bugs) access-member-before-definition, bad-super-call, maybe-no-member, no-member, no-method-argument, no-self-argument, not-callable, no-value-for-parameter, super-on-old-class, too-few-format-args, # "W" Warnings for stylistic problems or minor programming issues abstract-method, anomalous-backslash-in-string, anomalous-unicode-escape-in-string, arguments-differ, attribute-defined-outside-init, bad-builtin, bad-indentation, broad-except, dangerous-default-value, deprecated-lambda, duplicate-key, expression-not-assigned, fixme, global-statement, global-variable-not-assigned, logging-not-lazy, no-init, non-parent-init-called, pointless-string-statement, protected-access, redefined-builtin, redefined-outer-name, redefine-in-handler, signature-differs, star-args, super-init-not-called, unnecessary-lambda, unnecessary-pass, unpacking-non-sequence, unreachable, unused-argument, unused-import, unused-variable, # TODO(dougwig) - disable nonstandard-exception while we have neutron_lib shims nonstandard-exception, # "C" Coding convention violations bad-continuation, invalid-name, missing-docstring, old-style-class, superfluous-parens, # "R" Refactor recommendations abstract-class-little-used, abstract-class-not-used, duplicate-code, interface-not-implemented, no-self-use, too-few-public-methods, too-many-ancestors, too-many-arguments, too-many-branches, too-many-instance-attributes, too-many-lines, too-many-locals, too-many-public-methods, too-many-return-statements, too-many-statements [BASIC] # Variable names can be 1 to 31 characters long, with lowercase and underscores variable-rgx=[a-z_][a-z0-9_]{0,30}$ # Argument names can be 2 to 31 characters long, with lowercase and underscores argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long # and be lowecased with underscores method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$ # Module names matching neutron-* are ok (files in bin/) module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$ # Don't require docstrings on tests. no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ [FORMAT] # Maximum number of characters on a single line. max-line-length=79 [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. # _ is used by our localization additional-builtins=_ [CLASSES] # List of interface methods to ignore, separated by a comma. ignore-iface-methods= [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules= # should use oslo_serialization.jsonutils json [TYPECHECK] # List of module names for which member attributes should not be checked ignored-modules=six.moves,_MovedItems [REPORTS] # Tells whether to display a full report or only the messages reports=no neutron-dynamic-routing-16.0.0/releasenotes/0000775000175000017500000000000013656750704021127 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/notes/0000775000175000017500000000000013656750704022257 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000013656750631024527 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/notes/mp-bgp-support-d408e8569e94d07f.yaml0000664000175000017500000000040113656750631030212 0ustar zuulzuul00000000000000--- features: - | The os-ken driver now supports announcement of IPv6 prefixes over IPv4 sessions and vice-versa. Peers can opt in/out with MP-BGP capabilities configured on the peer end of BGP sessions established with the os-ken driver. neutron-dynamic-routing-16.0.0/releasenotes/notes/support-4byte-asn-d89d7100c0890ebf.yaml0000664000175000017500000000011513656750631030676 0ustar zuulzuul00000000000000--- features: - The neutron-dynamic-routing supports 4-byte AS Numbers now.neutron-dynamic-routing-16.0.0/releasenotes/notes/dvr-aware-announcements-24bfcb8fee87161d.yaml0000664000175000017500000000055213656750631032272 0ustar zuulzuul00000000000000--- features: - | DVR-aware BGP announcements are now supported for IPv4. Host routes for instances are announced as /32 host routes, using the appropriate floating IP gateway port on the host as the next-hop. This allows network traffic to bypass the centralized router on the network node and ingress/egress directly on the compute node. neutron-dynamic-routing-16.0.0/releasenotes/notes/drop-py27-support-795303ca12cccd34.yaml0000664000175000017500000000036013656750631030621 0ustar zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. Last release of neutron-dynamic-routing to support python 2.7 is OpenStack Train. The minimum version of Python now supported by neutron-dynamic-routing is Python 3.6. neutron-dynamic-routing-16.0.0/releasenotes/source/0000775000175000017500000000000013656750704022427 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/source/train.rst0000664000175000017500000000017613656750631024301 0ustar zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train neutron-dynamic-routing-16.0.0/releasenotes/source/_static/0000775000175000017500000000000013656750704024055 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000013656750631026325 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/source/conf.py0000664000175000017500000002207113656750631023727 0ustar zuulzuul00000000000000# 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. # Neutron Dynamic Routing Release Notes documentation build configuration file, # created by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options repository_name = 'neutron-dynamic-routing' bug_project = 'neutron' bug_tag = 'doc' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Neutron Dynamic Routing Release Notes' copyright = u'2015, Neutron Dynamic Routing Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' html_last_updated_fmt = '%Y-%m-%d %H:%M' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronDynamicRoutingReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'NeutronDynamicRoutingReleaseNotes.tex', u'Neutron Dynamic Routing Release Notes Documentation', u'Neutron Dynamic Routing Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'neutrondynamicroutingreleasenotes', u'Neutron Dynamic Routing', ' Release Notes Documentation', [u'Neutron Dynamic Routing Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'NeutronDynamicRoutingReleaseNotes', u'Neutron Dynamic Routing', ' Release Notes Documentation', u'Neutron Dynamic Routing Developers', 'NeutronDynamicRoutingReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] neutron-dynamic-routing-16.0.0/releasenotes/source/stein.rst0000664000175000017500000000022113656750631024275 0ustar zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein neutron-dynamic-routing-16.0.0/releasenotes/source/queens.rst0000664000175000017500000000022313656750631024455 0ustar zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens neutron-dynamic-routing-16.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000015613656750631025311 0ustar zuulzuul00000000000000============================= Current Series Release Notes ============================= .. release-notes:: neutron-dynamic-routing-16.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022113656750631024302 0ustar zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky neutron-dynamic-routing-16.0.0/releasenotes/source/README.rst0000664000175000017500000000104313656750631024113 0ustar zuulzuul00000000000000=========================================== Neutron Dynamic Routing Release Notes Howto =========================================== Release notes are a new feature for documenting new features in OpenStack projects. Background on the process, tooling, and methodology is documented in a `mailing list post by Doug Hellman `_. For information on how to create release notes, please consult the `Release Notes documentation `_. neutron-dynamic-routing-16.0.0/releasenotes/source/index.rst0000664000175000017500000000036013656750631024266 0ustar zuulzuul00000000000000====================================== Neutron Dynamic Routing Release Notes ====================================== .. toctree:: :maxdepth: 1 README.rst unreleased train stein rocky queens pike ocata newton neutron-dynamic-routing-16.0.0/releasenotes/source/ocata.rst0000664000175000017500000000023013656750631024242 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata neutron-dynamic-routing-16.0.0/releasenotes/source/newton.rst0000664000175000017500000000023213656750631024467 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton neutron-dynamic-routing-16.0.0/releasenotes/source/_templates/0000775000175000017500000000000013656750704024564 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013656750631027034 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/releasenotes/source/pike.rst0000664000175000017500000000021713656750631024110 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/0000775000175000017500000000000013656750704023403 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/__init__.py0000664000175000017500000000136413656750631025517 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import gettext import six if six.PY2: gettext.install('neutron', unicode=1) else: gettext.install('neutron') neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/version.py0000664000175000017500000000127613656750631025447 0ustar zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # # 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 pbr.version version_info = pbr.version.VersionInfo('neutron_dynamic_routing') neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/0000775000175000017500000000000013656750704025226 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/__init__.py0000664000175000017500000000000013656750631027324 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/0000775000175000017500000000000013656750704025776 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/__init__.py0000664000175000017500000000000013656750631030074 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/common/0000775000175000017500000000000013656750704027266 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/common/__init__.py0000664000175000017500000000000013656750631031364 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/common/opts.py0000664000175000017500000000174113656750631030627 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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 itertools import neutron_dynamic_routing.services.bgp.agent.config def list_bgp_agent_opts(): return [ ('bgp', itertools.chain( neutron_dynamic_routing.services.bgp.agent. config.BGP_DRIVER_OPTS, neutron_dynamic_routing.services.bgp.agent. config.BGP_PROTO_CONFIG_OPTS) ) ] neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/common/constants.py0000664000175000017500000000161213656750631031653 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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. AGENT_TYPE_BGP_ROUTING = 'BGP dynamic routing agent' BGP_DRAGENT = 'bgp_dragent' BGP_PLUGIN = 'q-bgp-plugin' # List of supported authentication types. SUPPORTED_AUTH_TYPES = ['none', 'md5'] # Supported AS number range MIN_ASNUM = 1 MAX_ASNUM = 65535 MAX_4BYTE_ASNUM = 4294967295 neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/0000775000175000017500000000000013656750704027074 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/entry.py0000664000175000017500000000337413656750631030615 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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 sys from oslo_config import cfg from oslo_service import service from neutron.common import config as common_config from neutron.conf.agent import common as config from neutron import service as neutron_service from neutron_dynamic_routing.services.bgp.agent import config as bgp_dragent_config # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa def register_options(): config.register_agent_state_opts_helper(cfg.CONF) config.register_root_helper(cfg.CONF) cfg.CONF.register_opts(bgp_dragent_config.BGP_DRIVER_OPTS, 'BGP') cfg.CONF.register_opts(bgp_dragent_config.BGP_PROTO_CONFIG_OPTS, 'BGP') config.register_external_process_opts(cfg.CONF) def main(): register_options() common_config.init(sys.argv[1:]) config.setup_logging() server = neutron_service.Service.create( binary='neutron-bgp-dragent', topic=bgp_consts.BGP_DRAGENT, report_interval=cfg.CONF.AGENT.report_interval, manager='neutron_dynamic_routing.services.bgp.agent.bgp_dragent.' 'BgpDrAgentWithStateReport') service.launch(cfg.CONF, server, restart_method='mutate').wait() neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/__init__.py0000664000175000017500000000000013656750631031172 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/bgp_dragent.py0000664000175000017500000007623513656750631031736 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import collections from neutron_lib.agent import constants as agent_consts from neutron_lib.agent import topics from neutron_lib import context from neutron_lib import rpc as n_rpc from neutron_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_service import periodic_task from oslo_utils import importutils from neutron.agent import rpc as agent_rpc from neutron.common import utils from neutron import manager from neutron_dynamic_routing.extensions import bgp as bgp_ext from neutron_dynamic_routing._i18n import _, _LE, _LI, _LW from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as driver_exc # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa LOG = logging.getLogger(__name__) class BgpDrAgent(manager.Manager): """BGP Dynamic Routing agent service manager. Note that the public methods of this class are exposed as the server side of an rpc interface. The neutron server uses api.rpc.agentnotifiers.bgp_dr_rpc_agent_api.BgpDrAgentNotifyApi as the client side to execute the methods here. For more information about changing rpc interfaces, see https://docs.openstack.org/neutron/latest/contributor/internals/rpc_api.html. API version history: 1.0 initial Version """ target = oslo_messaging.Target(version='1.0') def __init__(self, host, conf=None): super(BgpDrAgent, self).__init__() self.initialize_driver(conf) self.needs_resync_reasons = collections.defaultdict(list) self.needs_full_sync_reason = None self.cache = BgpSpeakerCache() self.context = context.get_admin_context_without_session() self.plugin_rpc = BgpDrPluginApi(bgp_consts.BGP_PLUGIN, self.context, host) def initialize_driver(self, conf): self.conf = conf or cfg.CONF.BGP try: self.dr_driver_cls = ( importutils.import_object(self.conf.bgp_speaker_driver, self.conf)) except ImportError: LOG.exception(_LE("Error while importing BGP speaker driver %s"), self.conf.bgp_speaker_driver) raise SystemExit(1) def _handle_driver_failure(self, bgp_speaker_id, method, driver_exec): self.schedule_resync(reason=driver_exec, speaker_id=bgp_speaker_id) LOG.error(_LE('Call to driver for BGP Speaker %(bgp_speaker)s ' '%(method)s has failed with exception ' '%(driver_exec)s.'), {'bgp_speaker': bgp_speaker_id, 'method': method, 'driver_exec': driver_exec}) def after_start(self): self.run() LOG.info(_LI("BGP Dynamic Routing agent started")) def run(self): """Activate BGP Dynamic Routing agent.""" self.sync_state(self.context) self.periodic_resync(self.context) @runtime.synchronized('bgp-dragent') def sync_state(self, context, full_sync=None, bgp_speakers=None): try: hosted_bgp_speakers = self.plugin_rpc.get_bgp_speakers(context) hosted_bgp_speaker_ids = [bgp_speaker['id'] for bgp_speaker in hosted_bgp_speakers] cached_bgp_speakers = self.cache.get_bgp_speaker_ids() for bgp_speaker_id in cached_bgp_speakers: if bgp_speaker_id not in hosted_bgp_speaker_ids: self.remove_bgp_speaker_from_dragent(bgp_speaker_id) resync_all = not bgp_speakers or full_sync only_bs = set() if resync_all else set(bgp_speakers) for hosted_bgp_speaker in hosted_bgp_speakers: hosted_bs_id = hosted_bgp_speaker['id'] if resync_all or hosted_bs_id in only_bs: if not self.cache.is_bgp_speaker_added(hosted_bs_id): self.safe_configure_dragent_for_bgp_speaker( hosted_bgp_speaker) continue self.sync_bgp_speaker(hosted_bgp_speaker) resync_reason = "Periodic route cache refresh" self.schedule_resync(speaker_id=hosted_bs_id, reason=resync_reason) except Exception as e: self.schedule_full_resync(reason=e) LOG.error(_LE('Unable to sync BGP speaker state.')) def sync_bgp_speaker(self, bgp_speaker): # sync BGP Speakers bgp_peer_ips = set( [bgp_peer['peer_ip'] for bgp_peer in bgp_speaker['peers']]) cached_bgp_peer_ips = set( self.cache.get_bgp_peer_ips(bgp_speaker['id'])) removed_bgp_peer_ips = cached_bgp_peer_ips - bgp_peer_ips for bgp_peer_ip in removed_bgp_peer_ips: self.remove_bgp_peer_from_bgp_speaker(bgp_speaker['id'], bgp_peer_ip) if bgp_peer_ips: self.add_bgp_peers_to_bgp_speaker(bgp_speaker) # sync advertise routes cached_adv_routes = self.cache.get_adv_routes(bgp_speaker['id']) adv_routes = bgp_speaker['advertised_routes'] if cached_adv_routes == adv_routes: return for cached_route in cached_adv_routes: if cached_route not in adv_routes: self.withdraw_route_via_bgp_speaker(bgp_speaker['id'], bgp_speaker['local_as'], cached_route) self.advertise_routes_via_bgp_speaker(bgp_speaker) @utils.exception_logger() def _periodic_resync_helper(self, context): """Resync the BgpDrAgent state at the configured interval.""" if self.needs_resync_reasons or self.needs_full_sync_reason: full_sync = self.needs_full_sync_reason reasons = self.needs_resync_reasons # Reset old reasons self.needs_full_sync_reason = None self.needs_resync_reasons = collections.defaultdict(list) if full_sync: LOG.debug("resync all: %(reason)s", {"reason": full_sync}) for bgp_speaker, reason in reasons.items(): LOG.debug("resync (%(bgp_speaker)s): %(reason)s", {"reason": reason, "bgp_speaker": bgp_speaker}) self.sync_state( context, full_sync=full_sync, bgp_speakers=reasons.keys()) # NOTE: spacing is set 1 sec. The actual interval is controlled # by neutron/service.py which defaults to CONF.periodic_interval @periodic_task.periodic_task(spacing=1) def periodic_resync(self, context): LOG.debug("Started periodic resync.") self._periodic_resync_helper(context) @runtime.synchronized('bgp-dr-agent') def bgp_speaker_create_end(self, context, payload): """Handle bgp_speaker_create_end notification event.""" bgp_speaker_id = payload['bgp_speaker']['id'] LOG.debug('Received BGP speaker create notification for ' 'speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) self.add_bgp_speaker_helper(bgp_speaker_id) @runtime.synchronized('bgp-dr-agent') def bgp_speaker_remove_end(self, context, payload): """Handle bgp_speaker_remove_end notification event.""" bgp_speaker_id = payload['bgp_speaker']['id'] LOG.debug('Received BGP speaker remove notification for ' 'speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) self.remove_bgp_speaker_from_dragent(bgp_speaker_id) @runtime.synchronized('bgp-dr-agent') def bgp_peer_association_end(self, context, payload): """Handle bgp_peer_association_end notification event.""" bgp_peer_id = payload['bgp_peer']['peer_id'] bgp_speaker_id = payload['bgp_peer']['speaker_id'] LOG.debug('Received BGP peer associate notification for ' 'speaker_id=%(speaker_id)s peer_id=%(peer_id)s ' 'from the neutron server.', {'speaker_id': bgp_speaker_id, 'peer_id': bgp_peer_id}) self.add_bgp_peer_helper(bgp_speaker_id, bgp_peer_id) @runtime.synchronized('bgp-dr-agent') def bgp_peer_disassociation_end(self, context, payload): """Handle bgp_peer_disassociation_end notification event.""" bgp_peer_ip = payload['bgp_peer']['peer_ip'] bgp_speaker_id = payload['bgp_peer']['speaker_id'] LOG.debug('Received BGP peer disassociate notification for ' 'speaker_id=%(speaker_id)s peer_ip=%(peer_ip)s ' 'from the neutron server.', {'speaker_id': bgp_speaker_id, 'peer_ip': bgp_peer_ip}) self.remove_bgp_peer_from_bgp_speaker(bgp_speaker_id, bgp_peer_ip) @runtime.synchronized('bgp-dr-agent') def bgp_routes_advertisement_end(self, context, payload): """Handle bgp_routes_advertisement_end notification event.""" bgp_speaker_id = payload['advertise_routes']['speaker_id'] LOG.debug('Received routes advertisement end notification ' 'for speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) routes = payload['advertise_routes']['routes'] self.add_routes_helper(bgp_speaker_id, routes) @runtime.synchronized('bgp-dr-agent') def bgp_routes_withdrawal_end(self, context, payload): """Handle bgp_routes_withdrawal_end notification event.""" bgp_speaker_id = payload['withdraw_routes']['speaker_id'] LOG.debug('Received route withdrawal notification for ' 'speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) routes = payload['withdraw_routes']['routes'] self.withdraw_routes_helper(bgp_speaker_id, routes) def add_bgp_speaker_helper(self, bgp_speaker_id): """Add BGP speaker.""" bgp_speaker = self.safe_get_bgp_speaker_info(bgp_speaker_id) if bgp_speaker: self.add_bgp_speaker_on_dragent(bgp_speaker) def add_bgp_peer_helper(self, bgp_speaker_id, bgp_peer_id): """Add BGP peer.""" # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return bgp_peer = self.safe_get_bgp_peer_info(bgp_speaker_id, bgp_peer_id) if bgp_peer: bgp_speaker_as = self.cache.get_bgp_speaker_local_as( bgp_speaker_id) self.add_bgp_peer_to_bgp_speaker(bgp_speaker_id, bgp_speaker_as, bgp_peer) def add_routes_helper(self, bgp_speaker_id, routes): """Advertise routes to BGP speaker.""" # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return bgp_speaker_as = self.cache.get_bgp_speaker_local_as(bgp_speaker_id) for route in routes: self.advertise_route_via_bgp_speaker(bgp_speaker_id, bgp_speaker_as, route) if self.is_resync_scheduled(bgp_speaker_id): break def withdraw_routes_helper(self, bgp_speaker_id, routes): """Withdraw routes advertised by BGP speaker.""" # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return bgp_speaker_as = self.cache.get_bgp_speaker_local_as(bgp_speaker_id) for route in routes: self.withdraw_route_via_bgp_speaker(bgp_speaker_id, bgp_speaker_as, route) if self.is_resync_scheduled(bgp_speaker_id): break def safe_get_bgp_speaker_info(self, bgp_speaker_id): try: bgp_speaker = self.plugin_rpc.get_bgp_speaker_info(self.context, bgp_speaker_id) if not bgp_speaker: LOG.warning(_LW('BGP Speaker %s has been deleted.'), bgp_speaker_id) return bgp_speaker except Exception as e: self.schedule_resync(speaker_id=bgp_speaker_id, reason=e) LOG.error(_LE('BGP Speaker %(bgp_speaker)s info call ' 'failed with reason=%(e)s.'), {'bgp_speaker': bgp_speaker_id, 'e': e}) def safe_get_bgp_peer_info(self, bgp_speaker_id, bgp_peer_id): try: bgp_peer = self.plugin_rpc.get_bgp_peer_info(self.context, bgp_peer_id) if not bgp_peer: LOG.warning(_LW('BGP Peer %s has been deleted.'), bgp_peer) return bgp_peer except Exception as e: self.schedule_resync(speaker_id=bgp_speaker_id, reason=e) LOG.error(_LE('BGP peer %(bgp_peer)s info call ' 'failed with reason=%(e)s.'), {'bgp_peer': bgp_peer_id, 'e': e}) @utils.exception_logger() def safe_configure_dragent_for_bgp_speaker(self, bgp_speaker): try: self.add_bgp_speaker_on_dragent(bgp_speaker) except (bgp_ext.BgpSpeakerNotFound, RuntimeError): LOG.warning(_LW('BGP speaker %s may have been deleted and its ' 'resources may have already been disposed.'), bgp_speaker['id']) def add_bgp_speaker_on_dragent(self, bgp_speaker): # Caching BGP speaker details in BGPSpeakerCache. Will be used # during smooth. self.cache.put_bgp_speaker(bgp_speaker) LOG.debug('Calling driver for adding BGP speaker %(speaker_id)s,' ' speaking for local_as %(local_as)s', {'speaker_id': bgp_speaker['id'], 'local_as': bgp_speaker['local_as']}) try: self.dr_driver_cls.add_bgp_speaker(bgp_speaker['local_as']) except driver_exc.BgpSpeakerAlreadyScheduled: return except Exception as e: self._handle_driver_failure(bgp_speaker['id'], 'add_bgp_speaker', e) # Add peer and route information to the driver. self.add_bgp_peers_to_bgp_speaker(bgp_speaker) self.advertise_routes_via_bgp_speaker(bgp_speaker) self.schedule_resync(speaker_id=bgp_speaker['id'], reason="Periodic route cache refresh") def remove_bgp_speaker_from_dragent(self, bgp_speaker_id): if self.cache.is_bgp_speaker_added(bgp_speaker_id): bgp_speaker_as = self.cache.get_bgp_speaker_local_as( bgp_speaker_id) self.cache.remove_bgp_speaker_by_id(bgp_speaker_id) LOG.debug('Calling driver for removing BGP speaker %(speaker_as)s', {'speaker_as': bgp_speaker_as}) try: self.dr_driver_cls.delete_bgp_speaker(bgp_speaker_as) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'remove_bgp_speaker', e) return # Ideally, only the added speakers can be removed by the neutron # server. Looks like there might be some synchronization # issue between the server and the agent. Let's initiate a re-sync # to resolve the issue. self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") def add_bgp_peers_to_bgp_speaker(self, bgp_speaker): for bgp_peer in bgp_speaker['peers']: self.add_bgp_peer_to_bgp_speaker(bgp_speaker['id'], bgp_speaker['local_as'], bgp_peer) if self.is_resync_scheduled(bgp_speaker['id']): break def add_bgp_peer_to_bgp_speaker(self, bgp_speaker_id, bgp_speaker_as, bgp_peer): if self.cache.get_bgp_peer_by_ip(bgp_speaker_id, bgp_peer['peer_ip']): return self.cache.put_bgp_peer(bgp_speaker_id, bgp_peer) LOG.debug('Calling driver interface for adding BGP peer %(peer_ip)s ' 'remote_as=%(remote_as)s to BGP Speaker running for ' 'local_as=%(local_as)d ' 'auth_type=%(auth_type)s', {'peer_ip': bgp_peer['peer_ip'], 'remote_as': bgp_peer['remote_as'], 'local_as': bgp_speaker_as, 'auth_type': bgp_peer['auth_type']}) try: self.dr_driver_cls.add_bgp_peer(bgp_speaker_as, bgp_peer['peer_ip'], bgp_peer['remote_as'], bgp_peer['auth_type'], bgp_peer['password']) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'add_bgp_peer', e) def remove_bgp_peer_from_bgp_speaker(self, bgp_speaker_id, bgp_peer_ip): # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return if self.cache.is_bgp_peer_added(bgp_speaker_id, bgp_peer_ip): self.cache.remove_bgp_peer_by_ip(bgp_speaker_id, bgp_peer_ip) bgp_speaker_as = self.cache.get_bgp_speaker_local_as( bgp_speaker_id) LOG.debug('Calling driver interface to remove BGP peer ' '%(peer_ip)s from BGP Speaker running for ' 'local_as=%(local_as)d', {'peer_ip': bgp_peer_ip, 'local_as': bgp_speaker_as}) try: self.dr_driver_cls.delete_bgp_peer(bgp_speaker_as, bgp_peer_ip) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'remove_bgp_peer', e) return # Ideally, only the added peers can be removed by the neutron # server. Looks like there might be some synchronization # issue between the server and the agent. Let's initiate a re-sync # to resolve the issue. self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Peer Out-of-sync") def advertise_routes_via_bgp_speaker(self, bgp_speaker): for route in bgp_speaker['advertised_routes']: self.advertise_route_via_bgp_speaker(bgp_speaker['id'], bgp_speaker['local_as'], route) if self.is_resync_scheduled(bgp_speaker['id']): break def advertise_route_via_bgp_speaker(self, bgp_speaker_id, bgp_speaker_as, route): if self.cache.is_route_advertised(bgp_speaker_id, route): # Requested route already advertised. Hence, Nothing to be done. return self.cache.put_adv_route(bgp_speaker_id, route) LOG.debug('Calling driver for advertising prefix: %(cidr)s, ' 'next_hop: %(nexthop)s', {'cidr': route['destination'], 'nexthop': route['next_hop']}) try: self.dr_driver_cls.advertise_route(bgp_speaker_as, route['destination'], route['next_hop']) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'advertise_route', e) def withdraw_route_via_bgp_speaker(self, bgp_speaker_id, bgp_speaker_as, route): if self.cache.is_route_advertised(bgp_speaker_id, route): self.cache.remove_adv_route(bgp_speaker_id, route) LOG.debug('Calling driver for withdrawing prefix: %(cidr)s, ' 'next_hop: %(nexthop)s', {'cidr': route['destination'], 'nexthop': route['next_hop']}) try: self.dr_driver_cls.withdraw_route(bgp_speaker_as, route['destination'], route['next_hop']) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'withdraw_route', e) return # Ideally, only the advertised routes can be withdrawn by the # neutron server. Looks like there might be some synchronization # issue between the server and the agent. Let's initiate a re-sync # to resolve the issue. self.schedule_resync(speaker_id=bgp_speaker_id, reason="Advertised routes Out-of-sync") def schedule_full_resync(self, reason): LOG.debug('Recording full resync request for all BGP Speakers ' 'with reason=%s', reason) self.needs_full_sync_reason = reason def schedule_resync(self, reason, speaker_id): """Schedule a full resync for a given BGP Speaker. If no BGP Speaker is specified, resync all BGP Speakers. """ LOG.debug('Recording resync request for BGP Speaker %s ' 'with reason=%s', speaker_id, reason) self.needs_resync_reasons[speaker_id].append(reason) def is_resync_scheduled(self, bgp_speaker_id): if bgp_speaker_id not in self.needs_resync_reasons: return False reason = self.needs_resync_reasons[bgp_speaker_id] # Re-sync scheduled for the queried BGP speaker. No point # continuing further. Let's stop processing and wait for # re-sync to happen. LOG.debug('Re-sync already scheduled for BGP Speaker %s ' 'with reason=%s', bgp_speaker_id, reason) return True class BgpDrPluginApi(object): """Agent side of BgpDrAgent RPC API. This class implements the client side of an rpc interface. The server side of this interface can be found in api.rpc.handlers.bgp_speaker_rpc.BgpSpeakerRpcCallback. For more information about changing rpc interfaces, see doc/source/devref/rpc_api.rst. API version history: 1.0 - Initial version. """ def __init__(self, topic, context, host): self.context = context self.host = host target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def get_bgp_speakers(self, context): """Make a remote process call to retrieve all BGP speakers info.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_bgp_speakers', host=self.host) def get_bgp_speaker_info(self, context, bgp_speaker_id): """Make a remote process call to retrieve a BGP speaker info.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_bgp_speaker_info', bgp_speaker_id=bgp_speaker_id) def get_bgp_peer_info(self, context, bgp_peer_id): """Make a remote process call to retrieve a BGP peer info.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_bgp_peer_info', bgp_peer_id=bgp_peer_id) class BgpSpeakerCache(object): """Agent cache of the current BGP speaker state. This class is designed to support the advertisement for multiple BGP speaker via a single driver interface. Version history: 1.0 - Initial version for caching the state of BGP speaker. """ def __init__(self): self.cache = {} def get_bgp_speaker_ids(self): return self.cache.keys() def put_bgp_speaker(self, bgp_speaker): if bgp_speaker['id'] in self.cache: self.remove_bgp_speaker_by_id(self.cache[bgp_speaker['id']]) self.cache[bgp_speaker['id']] = {'bgp_speaker': bgp_speaker, 'peers': {}, 'advertised_routes': []} def get_bgp_speaker_by_id(self, bgp_speaker_id): if bgp_speaker_id in self.cache: return self.cache[bgp_speaker_id]['bgp_speaker'] def get_bgp_speaker_local_as(self, bgp_speaker_id): bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) if bgp_speaker: return bgp_speaker['local_as'] def is_bgp_speaker_added(self, bgp_speaker_id): return self.get_bgp_speaker_by_id(bgp_speaker_id) def remove_bgp_speaker_by_id(self, bgp_speaker_id): if bgp_speaker_id in self.cache: del self.cache[bgp_speaker_id] def put_bgp_peer(self, bgp_speaker_id, bgp_peer): if bgp_peer['peer_ip'] in self.get_bgp_peer_ips(bgp_speaker_id): del self.cache[bgp_speaker_id]['peers'][bgp_peer['peer_ip']] self.cache[bgp_speaker_id]['peers'][bgp_peer['peer_ip']] = bgp_peer def is_bgp_peer_added(self, bgp_speaker_id, bgp_peer_ip): return self.get_bgp_peer_by_ip(bgp_speaker_id, bgp_peer_ip) def get_bgp_peer_ips(self, bgp_speaker_id): bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) if bgp_speaker: return self.cache[bgp_speaker_id]['peers'].keys() def get_bgp_peer_by_ip(self, bgp_speaker_id, bgp_peer_ip): bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) if bgp_speaker: return self.cache[bgp_speaker_id]['peers'].get(bgp_peer_ip) def remove_bgp_peer_by_ip(self, bgp_speaker_id, bgp_peer_ip): if bgp_peer_ip in self.get_bgp_peer_ips(bgp_speaker_id): del self.cache[bgp_speaker_id]['peers'][bgp_peer_ip] def put_adv_route(self, bgp_speaker_id, route): self.cache[bgp_speaker_id]['advertised_routes'].append(route) def is_route_advertised(self, bgp_speaker_id, route): routes = self.cache[bgp_speaker_id]['advertised_routes'] for r in routes: if r['destination'] == route['destination'] and ( route['next_hop'] is None or r['next_hop'] == route['next_hop']): return True return False def remove_adv_route(self, bgp_speaker_id, route): routes = self.cache[bgp_speaker_id]['advertised_routes'] updated_routes = [r for r in routes if ( r['destination'] != route['destination'])] self.cache[bgp_speaker_id]['advertised_routes'] = updated_routes def get_adv_routes(self, bgp_speaker_id): return self.cache[bgp_speaker_id]['advertised_routes'] def get_state(self): bgp_speaker_ids = self.get_bgp_speaker_ids() num_bgp_speakers = len(bgp_speaker_ids) num_bgp_peers = 0 num_advertised_routes = 0 for bgp_speaker_id in bgp_speaker_ids: bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) num_bgp_peers += len(bgp_speaker['peers']) num_advertised_routes += len(bgp_speaker['advertised_routes']) return {'bgp_speakers': num_bgp_speakers, 'bgp_peers': num_bgp_peers, 'advertise_routes': num_advertised_routes} class BgpDrAgentWithStateReport(BgpDrAgent): def __init__(self, host, conf=None): super(BgpDrAgentWithStateReport, self).__init__(host, conf) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) self.agent_state = { 'agent_type': bgp_consts.AGENT_TYPE_BGP_ROUTING, 'binary': 'neutron-bgp-dragent', 'configurations': {}, 'host': host, 'topic': bgp_consts.BGP_DRAGENT, 'start_flag': True} report_interval = cfg.CONF.AGENT.report_interval if report_interval: self.heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) self.heartbeat.start(interval=report_interval) def _report_state(self): LOG.debug("Report state task started") try: self.agent_state.get('configurations').update( self.cache.get_state()) ctx = context.get_admin_context_without_session() agent_status = self.state_rpc.report_state(ctx, self.agent_state, True) if agent_status == agent_consts.AGENT_REVIVED: LOG.info(_LI("Agent has just been revived. " "Scheduling full sync")) self.schedule_full_resync( reason=_("Agent has just been revived")) except AttributeError: # This means the server does not support report_state LOG.warning(_LW("Neutron server does not support state report. " "State report for this agent will be disabled.")) self.heartbeat.stop() self.run() return except Exception: LOG.exception(_LE("Failed reporting state!")) return if self.agent_state.pop('start_flag', None): self.run() def agent_updated(self, context, payload): """Handle the agent_updated notification event.""" self.schedule_full_resync( reason=_("BgpDrAgent updated: %s") % payload) LOG.info(_LI("agent_updated by server side %s!"), payload) def after_start(self): LOG.info(_LI("BGP dynamic routing agent started")) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/0000775000175000017500000000000013656750704030367 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/utils.py0000664000175000017500000000617513656750631032111 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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 netaddr import six from neutron_lib import constants as lib_consts from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa # Parameter validation functions provided are provided by the base. def validate_as_num(param, as_num): if not isinstance(as_num, six.integer_types): raise bgp_driver_exc.InvalidParamType(param=param, param_type='integer') if not (bgp_consts.MIN_ASNUM <= as_num <= bgp_consts.MAX_4BYTE_ASNUM): # Must be in [AS_NUM_MIN, MAX_4BYTE_ASNUM] range. allowed_range = ('[' + str(bgp_consts.MIN_ASNUM) + '-' + str(bgp_consts.MAX_4BYTE_ASNUM) + ']') raise bgp_driver_exc.InvalidParamRange(param=param, range=allowed_range) def validate_auth(auth_type, password): validate_string(password) if auth_type in bgp_consts.SUPPORTED_AUTH_TYPES: if auth_type != 'none' and password is None: raise bgp_driver_exc.PasswordNotSpecified(auth_type=auth_type) if auth_type == 'none' and password is not None: raise bgp_driver_exc.InvaildAuthType(auth_type=auth_type) else: raise bgp_driver_exc.InvaildAuthType(auth_type=auth_type) def validate_ip_addr(ip_addr): if netaddr.valid_ipv4(ip_addr): return lib_consts.IP_VERSION_4 elif netaddr.valid_ipv6(ip_addr): return lib_consts.IP_VERSION_6 else: raise bgp_driver_exc.InvalidParamType(param=ip_addr, param_type='ip-address') def validate_string(param): if param is not None: if not isinstance(param, six.string_types): raise bgp_driver_exc.InvalidParamType(param=param, param_type='string') class BgpMultiSpeakerCache(object): """Class for saving multiple BGP speakers information. Version history: 1.0 - Initial version for caching multiple BGP speaker information. """ def __init__(self): self.cache = {} def get_hosted_bgp_speakers_count(self): return len(self.cache) def put_bgp_speaker(self, local_as, speaker): self.cache[local_as] = speaker def get_bgp_speaker(self, local_as): return self.cache.get(local_as) def remove_bgp_speaker(self, local_as): self.cache.pop(local_as, None) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/__init__.py0000664000175000017500000000000013656750631032465 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/exceptions.py0000664000175000017500000000402613656750631033123 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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_lib import exceptions as n_exc from neutron_dynamic_routing._i18n import _ # BGP Driver Exceptions class BgpSpeakerNotAdded(n_exc.BadRequest): message = _("BGP Speaker for local_as=%(local_as)s with " "router_id=%(rtid)s not added yet.") class BgpSpeakerMaxScheduled(n_exc.BadRequest): message = _("Already hosting maximum number of BGP Speakers. " "Allowed scheduled count=%(count)d") class BgpSpeakerAlreadyScheduled(n_exc.Conflict): message = _("Already hosting BGP Speaker for local_as=%(current_as)d with " "router_id=%(rtid)s.") class BgpPeerNotAdded(n_exc.BadRequest): message = _("BGP Peer %(peer_ip)s for remote_as=%(remote_as)s, running " "for BGP Speaker %(speaker_as)d not added yet.") class RouteNotAdvertised(n_exc.BadRequest): message = _("Route %(cidr)s not advertised for BGP Speaker " "%(speaker_as)d.") class InvalidParamType(n_exc.NeutronException): message = _("Parameter %(param)s must be of %(param_type)s type.") class InvalidParamRange(n_exc.NeutronException): message = _("%(param)s must be in %(range)s range.") class InvaildAuthType(n_exc.BadRequest): message = _("Authentication type not supported. Requested " "type=%(auth_type)s.") class PasswordNotSpecified(n_exc.BadRequest): message = _("Password not specified for authentication " "type=%(auth_type)s.") neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/base.py0000664000175000017500000001374213656750631031661 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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 abc import six @six.add_metaclass(abc.ABCMeta) class BgpDriverBase(object): """Base class for BGP Speaking drivers. Any class which provides BGP functionality should extend this defined base class. """ @abc.abstractmethod def add_bgp_speaker(self, speaker_as): """Add a BGP speaker. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :raises: BgpSpeakerAlreadyScheduled, BgpSpeakerMaxScheduled, InvalidParamType, InvalidParamRange """ @abc.abstractmethod def delete_bgp_speaker(self, speaker_as): """Deletes BGP speaker. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :raises: BgpSpeakerNotAdded """ @abc.abstractmethod def add_bgp_peer(self, speaker_as, peer_ip, peer_as, auth_type='none', password=None): """Add a new BGP peer. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param peer_ip: Specifies the IP address of the peer. :type peer_ip: string :param peer_as: Specifies Autonomous Number of the peer. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type peer_as: biginteger :param auth_type: Specifies authentication type. By default, authentication will be disabled. :type auth_type: value in SUPPORTED_AUTH_TYPES :param password: Authentication password.By default, authentication will be disabled. :type password: string :raises: BgpSpeakerNotAdded, InvalidParamType, InvalidParamRange, InvaildAuthType, PasswordNotSpecified """ @abc.abstractmethod def delete_bgp_peer(self, speaker_as, peer_ip): """Delete a BGP peer associated with the given peer IP :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param peer_ip: Specifies the IP address of the peer. Must be the string representation of an IP address. :type peer_ip: string :raises: BgpSpeakerNotAdded, BgpPeerNotAdded """ @abc.abstractmethod def advertise_route(self, speaker_as, cidr, nexthop): """Add a new prefix to advertise. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param cidr: CIDR of the network to advertise. Must be the string representation of an IP network (e.g., 10.1.1.0/24) :type cidr: string :param nexthop: Specifies the next hop address for the above prefix. :type nexthop: string :raises: BgpSpeakerNotAdded, InvalidParamType """ @abc.abstractmethod def withdraw_route(self, speaker_as, cidr, nexthop=None): """Withdraw an advertised prefix. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param cidr: CIDR of the network to withdraw. Must be the string representation of an IP network (e.g., 10.1.1.0/24) :type cidr: string :param nexthop: Specifies the next hop address for the above prefix. :type nexthop: string :raises: BgpSpeakerNotAdded, RouteNotAdvertised, InvalidParamType """ @abc.abstractmethod def get_bgp_speaker_statistics(self, speaker_as): """Collect BGP Speaker statistics. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :raises: BgpSpeakerNotAdded :returns: bgp_speaker_stats: string """ @abc.abstractmethod def get_bgp_peer_statistics(self, speaker_as, peer_ip, peer_as): """Collect BGP Peer statistics. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param peer_ip: Specifies the IP address of the peer. :type peer_ip: string :param peer_as: Specifies the AS number of the peer. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type peer_as: biginteger . :raises: BgpSpeakerNotAdded, BgpPeerNotAdded :returns: bgp_peer_stats: string """ neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/os_ken/0000775000175000017500000000000013656750704031645 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/os_ken/__init__.py0000664000175000017500000000000013656750631033743 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/driver/os_ken/driver.py0000664000175000017500000002266513656750631033524 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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 os_ken.services.protocols.bgp import bgpspeaker from os_ken.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_ACTIVE from oslo_log import log as logging from oslo_utils import encodeutils from neutron_dynamic_routing._i18n import _LE, _LI from neutron_dynamic_routing.services.bgp.agent.driver import base from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.agent.driver import utils LOG = logging.getLogger(__name__) # Function for logging BGP peer and path changes. def bgp_peer_down_cb(remote_ip, remote_as): LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d went DOWN.'), {'peer_ip': remote_ip, 'peer_as': remote_as}) def bgp_peer_up_cb(remote_ip, remote_as): LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d is UP.'), {'peer_ip': remote_ip, 'peer_as': remote_as}) def best_path_change_cb(event): LOG.info(_LI("Best path change observed. cidr=%(prefix)s, " "nexthop=%(nexthop)s, remote_as=%(remote_as)d, " "is_withdraw=%(is_withdraw)s"), {'prefix': event.prefix, 'nexthop': event.nexthop, 'remote_as': event.remote_as, 'is_withdraw': event.is_withdraw}) class OsKenBgpDriver(base.BgpDriverBase): """BGP speaker implementation via os-ken.""" def __init__(self, cfg): LOG.info(_LI('Initializing os-ken driver for BGP functionality.')) self._read_config(cfg) # Note: Even though os-ken can only support one BGP speaker as of now, # we have tried making the framework generic for the future purposes. self.cache = utils.BgpMultiSpeakerCache() def _read_config(self, cfg): if cfg is None or cfg.bgp_router_id is None: # If either cfg or router_id is not specified, raise voice LOG.error(_LE('BGP router-id MUST be specified for the correct ' 'functional working.')) else: self.routerid = cfg.bgp_router_id LOG.info(_LI('Initialized os-ken BGP Speaker driver interface ' 'with bgp_router_id=%s'), self.routerid) def add_bgp_speaker(self, speaker_as): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if curr_speaker is not None: raise bgp_driver_exc.BgpSpeakerAlreadyScheduled( current_as=speaker_as, rtid=self.routerid) # os-ken can only support One speaker if self.cache.get_hosted_bgp_speakers_count() == 1: raise bgp_driver_exc.BgpSpeakerMaxScheduled(count=1) # Validate input parameters. # speaker_as must be an integer in the allowed range. utils.validate_as_num('local_as', speaker_as) # Notify os-ken about BGP Speaker addition. # Please note: Since, only the route-advertisement support is # implemented we are explicitly setting the bgp_server_port # attribute to 0 which disables listening on port 179. curr_speaker = bgpspeaker.BGPSpeaker(as_number=speaker_as, router_id=self.routerid, bgp_server_port=0, best_path_change_handler=best_path_change_cb, peer_down_handler=bgp_peer_down_cb, peer_up_handler=bgp_peer_up_cb) LOG.info(_LI('Added BGP Speaker for local_as=%(as)d with ' 'router_id= %(rtid)s.'), {'as': speaker_as, 'rtid': self.routerid}) self.cache.put_bgp_speaker(speaker_as, curr_speaker) def delete_bgp_speaker(self, speaker_as): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Notify os-ken about BGP Speaker deletion curr_speaker.shutdown() LOG.info(_LI('Removed BGP Speaker for local_as=%(as)d with ' 'router_id=%(rtid)s.'), {'as': speaker_as, 'rtid': self.routerid}) self.cache.remove_bgp_speaker(speaker_as) def add_bgp_peer(self, speaker_as, peer_ip, peer_as, auth_type='none', password=None): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate peer_ip and peer_as. utils.validate_as_num('remote_as', peer_as) utils.validate_ip_addr(peer_ip) utils.validate_auth(auth_type, password) if password is not None: password = encodeutils.to_utf8(password) curr_speaker.neighbor_add(address=peer_ip, remote_as=peer_as, enable_ipv4=True, enable_ipv6=True, password=password, connect_mode=CONNECT_MODE_ACTIVE) LOG.info(_LI('Added BGP Peer %(peer)s for remote_as=%(as)d to ' 'BGP Speaker running for local_as=%(local_as)d.'), {'peer': peer_ip, 'as': peer_as, 'local_as': speaker_as}) def delete_bgp_peer(self, speaker_as, peer_ip): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate peer_ip. It must be a string. utils.validate_ip_addr(peer_ip) # Notify os-ken about BGP Peer removal curr_speaker.neighbor_del(address=peer_ip) LOG.info(_LI('Removed BGP Peer %(peer)s from BGP Speaker ' 'running for local_as=%(local_as)d.'), {'peer': peer_ip, 'local_as': speaker_as}) def advertise_route(self, speaker_as, cidr, nexthop): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate cidr and nexthop. Both must be strings. utils.validate_string(cidr) utils.validate_string(nexthop) # Notify os-ken about route advertisement curr_speaker.prefix_add(prefix=cidr, next_hop=nexthop) LOG.info(_LI('Route cidr=%(prefix)s, nexthop=%(nexthop)s is ' 'advertised for BGP Speaker running for ' 'local_as=%(local_as)d.'), {'prefix': cidr, 'nexthop': nexthop, 'local_as': speaker_as}) def withdraw_route(self, speaker_as, cidr, nexthop=None): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate cidr. It must be a string. utils.validate_string(cidr) # Notify os-ken about route withdrawal curr_speaker.prefix_del(prefix=cidr) LOG.info(_LI('Route cidr=%(prefix)s is withdrawn from BGP Speaker ' 'running for local_as=%(local_as)d.'), {'prefix': cidr, 'local_as': speaker_as}) def get_bgp_speaker_statistics(self, speaker_as): LOG.info(_LI('Collecting BGP Speaker statistics for local_as=%d.'), speaker_as) curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # TODO(vikram): Filter and return the necessary information. # Will be done as part of new RFE requirement # https://bugs.launchpad.net/neutron/+bug/1527993 return curr_speaker.neighbor_state_get() def get_bgp_peer_statistics(self, speaker_as, peer_ip): LOG.info(_LI('Collecting BGP Peer statistics for peer_ip=%(peer)s, ' 'running in speaker_as=%(speaker_as)d '), {'peer': peer_ip, 'speaker_as': speaker_as}) curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # TODO(vikram): Filter and return the necessary information. # Will be done as part of new RFE requirement # https://bugs.launchpad.net/neutron/+bug/1527993 return curr_speaker.neighbor_state_get(address=peer_ip) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/agent/config.py0000664000175000017500000000176313656750631030721 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg from neutron_dynamic_routing._i18n import _ BGP_DRIVER_OPTS = [ cfg.StrOpt('bgp_speaker_driver', help=_("BGP speaker driver class to be instantiated.")) ] BGP_PROTO_CONFIG_OPTS = [ cfg.StrOpt('bgp_router_id', help=_("32-bit BGP identifier, typically an IPv4 address " "owned by the system running the BGP DrAgent.")) ] neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/bgp_plugin.py0000664000175000017500000005067413656750631030511 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # 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 netaddr import IPAddress from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib import context from neutron_lib import rpc as n_rpc from neutron_lib.services import base as service_base from oslo_config import cfg from oslo_log import log as logging from oslo_utils import importutils from neutron_dynamic_routing.api.rpc.agentnotifiers import bgp_dr_rpc_agent_api # noqa from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.api.rpc.handlers import bgp_speaker_rpc as bs_rpc from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db from neutron_dynamic_routing.extensions import bgp as bgp_ext from neutron_dynamic_routing.extensions import bgp_4byte_asn from neutron_dynamic_routing.extensions import bgp_dragentscheduler as dras_ext from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts PLUGIN_NAME = bgp_ext.BGP_EXT_ALIAS + '_svc_plugin' LOG = logging.getLogger(__name__) class BgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin, bgp_dragentscheduler_db.BgpDrAgentSchedulerDbMixin): supported_extension_aliases = [bgp_ext.BGP_EXT_ALIAS, dras_ext.BGP_DRAGENT_SCHEDULER_EXT_ALIAS, bgp_4byte_asn.BGP_4BYTE_ASN_EXT_ALIAS] def __init__(self): super(BgpPlugin, self).__init__() self.bgp_drscheduler = importutils.import_object( cfg.CONF.bgp_drscheduler_driver) self._setup_rpc() self._register_callbacks() self.add_periodic_dragent_status_check() def get_plugin_type(self): return bgp_ext.BGP_EXT_ALIAS def get_plugin_description(self): """returns string description of the plugin.""" return ("BGP dynamic routing service for announcement of next-hops " "for project networks, floating IP's, and DVR host routes.") def _setup_rpc(self): self.topic = bgp_consts.BGP_PLUGIN self.conn = n_rpc.Connection() self.agent_notifiers[bgp_consts.AGENT_TYPE_BGP_ROUTING] = ( bgp_dr_rpc_agent_api.BgpDrAgentNotifyApi() ) self._bgp_rpc = self.agent_notifiers[bgp_consts.AGENT_TYPE_BGP_ROUTING] self.endpoints = [bs_rpc.BgpSpeakerRpcCallback()] self.conn.create_consumer(self.topic, self.endpoints, fanout=False) self.conn.consume_in_threads() def _register_callbacks(self): registry.subscribe(self.floatingip_update_callback, resources.FLOATING_IP, events.AFTER_UPDATE) registry.subscribe(self.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_CREATE) registry.subscribe(self.router_interface_callback, resources.ROUTER_INTERFACE, events.BEFORE_CREATE) registry.subscribe(self.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_DELETE) registry.subscribe(self.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_CREATE) registry.subscribe(self.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_DELETE) registry.subscribe(self.port_callback, resources.PORT, events.AFTER_UPDATE) def get_bgp_speakers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): return super(BgpPlugin, self).get_bgp_speakers( context, filters=filters, fields=fields, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) def get_bgp_speaker(self, context, bgp_speaker_id, fields=None): return super(BgpPlugin, self).get_bgp_speaker(context, bgp_speaker_id, fields=fields) def create_bgp_speaker(self, context, bgp_speaker): bgp_speaker = super(BgpPlugin, self).create_bgp_speaker(context, bgp_speaker) payload = {'plugin': self, 'context': context, 'bgp_speaker': bgp_speaker} registry.notify(dr_resources.BGP_SPEAKER, events.AFTER_CREATE, self, payload=payload) return bgp_speaker def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker): return super(BgpPlugin, self).update_bgp_speaker(context, bgp_speaker_id, bgp_speaker) def delete_bgp_speaker(self, context, bgp_speaker_id): hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) super(BgpPlugin, self).delete_bgp_speaker(context, bgp_speaker_id) for agent in hosted_bgp_dragents: self._bgp_rpc.bgp_speaker_removed(context, bgp_speaker_id, agent.host) def get_bgp_peers(self, context, fields=None, filters=None, sorts=None, limit=None, marker=None, page_reverse=False): return super(BgpPlugin, self).get_bgp_peers( context, fields=fields, filters=filters, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) def get_bgp_peer(self, context, bgp_peer_id, fields=None): return super(BgpPlugin, self).get_bgp_peer(context, bgp_peer_id, fields=fields) def create_bgp_peer(self, context, bgp_peer): return super(BgpPlugin, self).create_bgp_peer(context, bgp_peer) def update_bgp_peer(self, context, bgp_peer_id, bgp_peer): return super(BgpPlugin, self).update_bgp_peer(context, bgp_peer_id, bgp_peer) def delete_bgp_peer(self, context, bgp_peer_id): super(BgpPlugin, self).delete_bgp_peer(context, bgp_peer_id) def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): ret_value = super(BgpPlugin, self).add_bgp_peer(context, bgp_speaker_id, bgp_peer_info) hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) for agent in hosted_bgp_dragents: self._bgp_rpc.bgp_peer_associated(context, bgp_speaker_id, ret_value['bgp_peer_id'], agent.host) return ret_value def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) ret_value = super(BgpPlugin, self).remove_bgp_peer(context, bgp_speaker_id, bgp_peer_info) for agent in hosted_bgp_dragents: self._bgp_rpc.bgp_peer_disassociated(context, bgp_speaker_id, ret_value['bgp_peer_id'], agent.host) def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id): super(BgpPlugin, self).add_bgp_speaker_to_dragent(context, agent_id, speaker_id) def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): super(BgpPlugin, self).remove_bgp_speaker_from_dragent(context, agent_id, speaker_id) def list_bgp_speaker_on_dragent(self, context, agent_id): return super(BgpPlugin, self).list_bgp_speaker_on_dragent(context, agent_id) def list_dragent_hosting_bgp_speaker(self, context, speaker_id): return super(BgpPlugin, self).list_dragent_hosting_bgp_speaker( context, speaker_id) def add_gateway_network(self, context, bgp_speaker_id, network_info): return super(BgpPlugin, self).add_gateway_network(context, bgp_speaker_id, network_info) def remove_gateway_network(self, context, bgp_speaker_id, network_info): return super(BgpPlugin, self).remove_gateway_network(context, bgp_speaker_id, network_info) def get_advertised_routes(self, context, bgp_speaker_id): return super(BgpPlugin, self).get_advertised_routes(context, bgp_speaker_id) def floatingip_update_callback(self, resource, event, trigger, **kwargs): if event != events.AFTER_UPDATE: return ctx = context.get_admin_context() new_router_id = kwargs['router_id'] last_router_id = kwargs.get('last_known_router_id') floating_ip_address = kwargs['floating_ip_address'] dest = floating_ip_address + '/32' bgp_speakers = self._bgp_speakers_for_gw_network_by_family( ctx, kwargs['floating_network_id'], n_const.IP_VERSION_4) if last_router_id and new_router_id != last_router_id: # Here gives the old route next_hop a `None` value, then # the DR agent side will withdraw it. old_host_route = {'destination': dest, 'next_hop': None} for bgp_speaker in bgp_speakers: self.stop_route_advertisements(ctx, self._bgp_rpc, bgp_speaker.id, [old_host_route]) if new_router_id and new_router_id != last_router_id: next_hop = self._get_fip_next_hop( ctx, new_router_id, floating_ip_address) new_host_route = {'destination': dest, 'next_hop': next_hop} for bgp_speaker in bgp_speakers: self.start_route_advertisements(ctx, self._bgp_rpc, bgp_speaker.id, [new_host_route]) def router_interface_callback(self, resource, event, trigger, **kwargs): if event == events.AFTER_CREATE: self._handle_router_interface_after_create(**kwargs) if event == events.AFTER_DELETE: gw_network = kwargs['network_id'] next_hops = self._next_hops_from_gateway_ips( kwargs['gateway_ips']) ctx = context.get_admin_context() speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) for speaker in speakers: routes = self._route_list_from_prefixes_and_next_hop( kwargs['cidrs'], next_hops[speaker.ip_version]) self._handle_router_interface_after_delete(gw_network, routes) def _handle_router_interface_after_create(self, **kwargs): gw_network = kwargs['network_id'] if not gw_network: return ctx = context.get_admin_context() with ctx.session.begin(subtransactions=True): speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) next_hops = self._next_hops_from_gateway_ips( kwargs['gateway_ips']) for speaker in speakers: prefixes = self._tenant_prefixes_by_router( ctx, kwargs['router_id'], speaker.id) next_hop = next_hops.get(speaker.ip_version) if next_hop: rl = self._route_list_from_prefixes_and_next_hop(prefixes, next_hop) self.start_route_advertisements(ctx, self._bgp_rpc, speaker.id, rl) def router_gateway_callback(self, resource, event, trigger, payload=None): if event == events.AFTER_CREATE: self._handle_router_gateway_after_create(payload) if event == events.AFTER_DELETE: gw_network = payload.metadata.get('network_id') router_id = payload.resource_id next_hops = self._next_hops_from_gateway_ips( payload.metadata.get('gateway_ips')) ctx = context.get_admin_context() speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) for speaker in speakers: if speaker.ip_version in next_hops: next_hop = next_hops[speaker.ip_version] prefixes = self._tenant_prefixes_by_router(ctx, router_id, speaker.id) routes = self._route_list_from_prefixes_and_next_hop( prefixes, next_hop) self._handle_router_interface_after_delete(gw_network, routes) def _handle_router_gateway_after_create(self, payload): ctx = context.get_admin_context() gw_network = payload.metadata.get('network_id') router_id = payload.resource_id with ctx.session.begin(subtransactions=True): speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) next_hops = self._next_hops_from_gateway_ips( payload.metadata.get('gateway_ips')) for speaker in speakers: if speaker.ip_version in next_hops: next_hop = next_hops[speaker.ip_version] prefixes = self._tenant_prefixes_by_router(ctx, router_id, speaker.id) routes = self._route_list_from_prefixes_and_next_hop( prefixes, next_hop) self.start_route_advertisements(ctx, self._bgp_rpc, speaker.id, routes) def _handle_router_interface_after_delete(self, gw_network, routes): if gw_network and routes: ctx = context.get_admin_context() speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) for speaker in speakers: self.stop_route_advertisements(ctx, self._bgp_rpc, speaker.id, routes) def port_callback(self, resource, event, trigger, **kwargs): if event != events.AFTER_UPDATE: return original_port = kwargs['original_port'] updated_port = kwargs['port'] if not updated_port.get('fixed_ips'): return original_host = original_port.get(portbindings.HOST_ID) updated_host = updated_port.get(portbindings.HOST_ID) device_owner = updated_port.get('device_owner') # if host in the port binding has changed, update next-hops if original_host != updated_host and bool('compute:' in device_owner): ctx = context.get_admin_context() with ctx.session.begin(subtransactions=True): ext_nets = self.get_external_networks_for_port(ctx, updated_port) for ext_net in ext_nets: bgp_speakers = ( self._get_bgp_speaker_ids_by_binding_network( ctx, ext_nets)) # Refresh any affected BGP speakers for bgp_speaker in bgp_speakers: routes = self.get_advertised_routes(ctx, bgp_speaker) self.start_route_advertisements(ctx, self._bgp_rpc, bgp_speaker, routes) def _next_hops_from_gateway_ips(self, gw_ips): if gw_ips: return {IPAddress(ip).version: ip for ip in gw_ips} return {} def start_route_advertisements(self, ctx, bgp_rpc, bgp_speaker_id, routes): agents = self.list_dragent_hosting_bgp_speaker(ctx, bgp_speaker_id) for agent in agents['agents']: bgp_rpc.bgp_routes_advertisement(ctx, bgp_speaker_id, routes, agent['host']) msg = "Starting route advertisements for %s on BgpSpeaker %s" self._debug_log_for_routes(msg, routes, bgp_speaker_id) def stop_route_advertisements(self, ctx, bgp_rpc, bgp_speaker_id, routes): agents = self.list_dragent_hosting_bgp_speaker(ctx, bgp_speaker_id) for agent in agents['agents']: bgp_rpc.bgp_routes_withdrawal(ctx, bgp_speaker_id, routes, agent['host']) msg = "Stopping route advertisements for %s on BgpSpeaker %s" self._debug_log_for_routes(msg, routes, bgp_speaker_id) def _debug_log_for_routes(self, msg, routes, bgp_speaker_id): # Could have a large number of routes passed, check log level first if LOG.isEnabledFor(logging.DEBUG): for route in routes: LOG.debug(msg, route, bgp_speaker_id) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/scheduler/0000775000175000017500000000000013656750704027754 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/scheduler/__init__.py0000664000175000017500000000000013656750631032052 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/scheduler/bgp_dragent_scheduler.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/services/bgp/scheduler/bgp_dragent_scheduler.0000664000175000017500000002217013656750631034270 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context as nl_context from neutron_lib.objects import registry as obj_reg from oslo_db import exception as db_exc from oslo_log import log as logging from sqlalchemy import sql from neutron.agent.common import utils from neutron.db.models import agent as agent_model from neutron.scheduler import base_resource_filter from neutron.scheduler import base_scheduler from neutron_dynamic_routing._i18n import _LI, _LW from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts LOG = logging.getLogger(__name__) BGP_SPEAKER_PER_DRAGENT = 1 class BgpDrAgentFilter(base_resource_filter.BaseResourceFilter): def bind(self, context, agents, bgp_speaker_id, force_scheduling=False): """Bind the BgpSpeaker to a BgpDrAgent.""" bound_agents = agents[:] for agent in agents: # saving agent_id to use it after rollback to avoid # DetachedInstanceError agent_id = agent.id binding = bgp_dras_db.BgpSpeakerDrAgentBinding() binding.agent_id = agent_id binding.bgp_speaker_id = bgp_speaker_id try: with context.session.begin(subtransactions=True): context.session.add(binding) except db_exc.DBDuplicateEntry: # it's totally ok, someone just did our job! bound_agents.remove(agent) LOG.info(_LI('BgpDrAgent %s already present'), agent_id) LOG.debug('BgpSpeaker %(bgp_speaker_id)s is scheduled to be ' 'hosted by BgpDrAgent %(agent_id)s', {'bgp_speaker_id': bgp_speaker_id, 'agent_id': agent_id}) super(BgpDrAgentFilter, self).bind(context, bound_agents, bgp_speaker_id) # TODO(frickler): once neutron is released, switch to this # super(BgpDrAgentFilter, self).bind(context, bound_agents, # bgp_speaker_id, force_scheduling) def filter_agents(self, plugin, context, bgp_speaker): """Return the agents that can host the BgpSpeaker.""" agents_dict = self._get_bgp_speaker_hostable_dragents( plugin, context, bgp_speaker) if not agents_dict['hostable_agents'] or agents_dict['n_agents'] <= 0: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} return agents_dict def _get_active_dragents(self, plugin, context): """Return a list of active BgpDrAgents.""" with context.session.begin(subtransactions=True): active_dragents = plugin.get_agent_objects( context, filters={ 'agent_type': [bgp_consts.AGENT_TYPE_BGP_ROUTING], 'admin_state_up': [True]}) if not active_dragents: return [] return active_dragents def _get_num_dragents_hosting_bgp_speaker(self, bgp_speaker_id, dragent_bindings): return sum(1 if dragent_binding.bgp_speaker_id == bgp_speaker_id else 0 for dragent_binding in dragent_bindings) def _get_bgp_speaker_hostable_dragents(self, plugin, context, bgp_speaker): """Return number of additional BgpDrAgents which will actually host the given BgpSpeaker and a list of BgpDrAgents which can host the given BgpSpeaker """ # only one BgpSpeaker can be hosted by a BgpDrAgent for now. dragents_per_bgp_speaker = BGP_SPEAKER_PER_DRAGENT dragent_bindings = plugin.get_dragent_bgp_speaker_bindings(context) agents_hosting = [dragent_binding.agent_id for dragent_binding in dragent_bindings] num_dragents_hosting_bgp_speaker = ( self._get_num_dragents_hosting_bgp_speaker(bgp_speaker['id'], dragent_bindings)) n_agents = dragents_per_bgp_speaker - num_dragents_hosting_bgp_speaker if n_agents <= 0: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} active_dragents = self._get_active_dragents(plugin, context) hostable_dragents = [ agent for agent in set(active_dragents) if agent.id not in agents_hosting and plugin.is_eligible_agent( active=True, agent=agent) ] if not hostable_dragents: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} n_agents = min(len(hostable_dragents), n_agents) return {'n_agents': n_agents, 'hostable_agents': hostable_dragents, 'hosted_agents': num_dragents_hosting_bgp_speaker} class BgpDrAgentSchedulerBase(BgpDrAgentFilter): def _register_callbacks(self): registry.subscribe(self.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE) def schedule_all_unscheduled_bgp_speakers(self, context): """Call schedule_unscheduled_bgp_speakers for all hosts. """ with context.session.begin(subtransactions=True): query = context.session.query(agent_model.Agent.host).distinct() for agent in query: self.schedule_unscheduled_bgp_speakers(context, agent[0]) return True def schedule_unscheduled_bgp_speakers(self, context, host): """Schedule unscheduled BgpSpeaker to a BgpDrAgent. """ LOG.debug('Started auto-scheduling on host %s', host) with context.session.begin(subtransactions=True): bgp_dragent = obj_reg.load_class('Agent').get_object( context, agent_type=bgp_consts.AGENT_TYPE_BGP_ROUTING, host=host, admin_state_up=True) if not bgp_dragent: LOG.debug('No enabled BgpDrAgent on host %s', host) return False if utils.is_agent_down( bgp_dragent.heartbeat_timestamp): LOG.warning(_LW('BgpDrAgent %s is down'), bgp_dragent.id) return False if self._is_bgp_speaker_hosted(context, bgp_dragent['id']): # One BgpDrAgent can only host one BGP speaker LOG.debug('BgpDrAgent already hosting a speaker on host %s. ' 'Cannot schedule an another one', host) return False unscheduled_speakers = self._get_unscheduled_bgp_speakers(context) if not unscheduled_speakers: LOG.debug('Nothing to auto-schedule on host %s', host) return False self.bind(context, [bgp_dragent], unscheduled_speakers[0]) return True def _is_bgp_speaker_hosted(self, context, agent_id): speaker_binding_model = bgp_dras_db.BgpSpeakerDrAgentBinding query = context.session.query(speaker_binding_model) query = query.filter(speaker_binding_model.agent_id == agent_id) return query.count() > 0 def _get_unscheduled_bgp_speakers(self, context): """BGP speakers that needs to be scheduled. """ no_agent_binding = ~sql.exists().where( bgp_db.BgpSpeaker.id == bgp_dras_db.BgpSpeakerDrAgentBinding.bgp_speaker_id) query = context.session.query(bgp_db.BgpSpeaker.id).filter( no_agent_binding) return [bgp_speaker_id_[0] for bgp_speaker_id_ in query] def schedule_bgp_speaker_callback(self, resource, event, trigger, payload): plugin = payload['plugin'] if event == events.AFTER_CREATE: ctx = nl_context.get_admin_context() plugin.schedule_bgp_speaker(ctx, payload['bgp_speaker']) class ChanceScheduler(base_scheduler.BaseChanceScheduler, BgpDrAgentSchedulerBase): def __init__(self): super(ChanceScheduler, self).__init__(self) self._register_callbacks() class WeightScheduler(base_scheduler.BaseWeightScheduler, BgpDrAgentSchedulerBase): def __init__(self): super(WeightScheduler, self).__init__(self) self._register_callbacks() neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/policies/0000775000175000017500000000000013656750704025212 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/policies/__init__.py0000664000175000017500000000162013656750631027321 0ustar zuulzuul00000000000000# 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 itertools from neutron_dynamic_routing.policies import bgp_dragent from neutron_dynamic_routing.policies import bgp_peer from neutron_dynamic_routing.policies import bgp_speaker def list_rules(): return itertools.chain( bgp_speaker.list_rules(), bgp_peer.list_rules(), bgp_dragent.list_rules(), ) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/policies/bgp_dragent.py0000664000175000017500000000367713656750631030054 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from neutron_dynamic_routing.policies import base rules = [ policy.DocumentedRuleDefault( 'add_bgp_speaker_to_dragent', base.RULE_ADMIN_ONLY, 'Add a BGP speaker to a dynamic routing agent', [ { 'method': 'POST', 'path': '/agents/{agent_id}/bgp-drinstances', }, ] ), policy.DocumentedRuleDefault( 'remove_bgp_speaker_from_dragent', base.RULE_ADMIN_ONLY, 'Remove a BGP speaker from a dynamic routing agent', [ { 'method': 'DELETE', 'path': '/agents/{agent_id}/bgp-drinstances/{bgp_speaker_id}', }, ] ), policy.DocumentedRuleDefault( 'list_bgp_speaker_on_dragent', base.RULE_ADMIN_ONLY, 'List BGP speakers hosted by a dynamic routing agent', [ { 'method': 'GET', 'path': '/agents/{agent_id}/bgp-drinstances', }, ] ), policy.DocumentedRuleDefault( 'list_dragent_hosting_bgp_speaker', base.RULE_ADMIN_ONLY, 'List dynamic routing agents hosting a BGP speaker', [ { 'method': 'GET', 'path': '/bgp-speakers/{bgp_speaker_id}/bgp-dragents', }, ] ), ] def list_rules(): return rules neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/policies/bgp_peer.py0000664000175000017500000000337413656750631027355 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from neutron_dynamic_routing.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgp_peer', base.RULE_ADMIN_ONLY, 'Create a BGP peer', [ { 'method': 'POST', 'path': '/bgp-peers', }, ] ), policy.DocumentedRuleDefault( 'update_bgp_peer', base.RULE_ADMIN_ONLY, 'Update a BGP peer', [ { 'method': 'PUT', 'path': '/bgp-peers/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_bgp_peer', base.RULE_ADMIN_ONLY, 'Delete a BGP peer', [ { 'method': 'DELETE', 'path': '/bgp-peers/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgp_peer', base.RULE_ADMIN_ONLY, 'Get BGP peers', [ { 'method': 'GET', 'path': '/bgp-peers', }, { 'method': 'GET', 'path': '/bgp-peers/{id}', }, ] ), ] def list_rules(): return rules neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/policies/base.py0000664000175000017500000000132313656750631026474 0ustar zuulzuul00000000000000# 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. # TODO(amotoki): Define these in neutron or neutron-lib RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner' RULE_ADMIN_ONLY = 'rule:admin_only' RULE_ANY = 'rule:regular_user' neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/policies/bgp_speaker.py0000664000175000017500000000636013656750631030052 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from neutron_dynamic_routing.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgp_speaker', base.RULE_ADMIN_ONLY, 'Create a BGP speaker', [ { 'method': 'POST', 'path': '/bgp-speakers', }, ] ), policy.DocumentedRuleDefault( 'update_bgp_speaker', base.RULE_ADMIN_ONLY, 'Update a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_bgp_speaker', base.RULE_ADMIN_ONLY, 'Delete a BGP speaker', [ { 'method': 'DELETE', 'path': '/bgp-speakers/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgp_speaker', base.RULE_ADMIN_ONLY, 'Get BGP speakers', [ { 'method': 'GET', 'path': '/bgp-speakers', }, { 'method': 'GET', 'path': '/bgp-speakers/{id}', }, ] ), policy.DocumentedRuleDefault( 'add_bgp_peer', base.RULE_ADMIN_ONLY, 'Add a BGP peer to a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/add_bgp_peer', }, ] ), policy.DocumentedRuleDefault( 'remove_bgp_peer', base.RULE_ADMIN_ONLY, 'Remove a BGP peer from a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/remove_bgp_peer', }, ] ), policy.DocumentedRuleDefault( 'add_gateway_network', base.RULE_ADMIN_ONLY, 'Add a gateway network to a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/add_gateway_network', }, ] ), policy.DocumentedRuleDefault( 'remove_gateway_network', base.RULE_ADMIN_ONLY, 'Remove a gateway network from a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/remove_gateway_network', }, ] ), policy.DocumentedRuleDefault( 'get_advertised_routes', base.RULE_ADMIN_ONLY, 'Get advertised routes of a BGP speaker', [ { 'method': 'GET', 'path': '/bgp-speakers/{id}/get_advertised_routes', }, ] ), ] def list_rules(): return rules neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/0000775000175000017500000000000013656750704024545 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/__init__.py0000664000175000017500000000000013656750631026643 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/0000775000175000017500000000000013656750704025524 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/__init__.py0000664000175000017500000000000013656750631027622 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/0000775000175000017500000000000013656750704027347 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/__init__.py0000664000175000017500000000000013656750631031445 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/0000775000175000017500000000000013656750704030117 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/test_bgp_plugin.py0000664000175000017500000001042313656750631033655 0ustar zuulzuul00000000000000# Copyright (C) 2017 VA Linux Systems Japan K.K. # Copyright (C) 2017 Fumihiko Kakuma # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from neutron.tests import base from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.services.bgp import bgp_plugin class TestBgpPlugin(base.BaseTestCase): def setUp(self): super(TestBgpPlugin, self).setUp() bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.' 'agentnotifiers.bgp_dr_rpc_agent_api.' 'BgpDrAgentNotifyApi') bgp_notify_p.start() rpc_conn_p = mock.patch('neutron_lib.rpc.Connection') rpc_conn_p.start() admin_ctx_p = mock.patch('neutron_lib.context.get_admin_context') self.admin_ctx_m = admin_ctx_p.start() self.fake_admin_ctx = mock.Mock() self.admin_ctx_m.return_value = self.fake_admin_ctx self.plugin = bgp_plugin.BgpPlugin() def _create_test_payload(self, context='test_ctx'): bgp_speaker = {'id': '11111111-2222-3333-4444-555555555555'} payload = {'plugin': self.plugin, 'context': context, 'bgp_speaker': bgp_speaker} return payload def test__register_callbacks(self): with mock.patch.object(registry, 'subscribe') as subscribe: plugin = bgp_plugin.BgpPlugin() expected_calls = [ mock.call(plugin.bgp_drscheduler.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE), mock.call(plugin.floatingip_update_callback, resources.FLOATING_IP, events.AFTER_UPDATE), mock.call(plugin.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_CREATE), mock.call(plugin.router_interface_callback, resources.ROUTER_INTERFACE, events.BEFORE_CREATE), mock.call(plugin.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_DELETE), mock.call(plugin.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_CREATE), mock.call(plugin.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_DELETE), mock.call(plugin.port_callback, resources.PORT, events.AFTER_UPDATE), ] self.assertEqual(subscribe.call_args_list, expected_calls) def test_create_bgp_speaker(self): test_context = 'create_bgp_context' test_bgp_speaker = {'id': None} payload = self._create_test_payload(context=test_context) with mock.patch.object(bgp_db.BgpDbMixin, 'create_bgp_speaker') as create_bgp_sp: with mock.patch.object(registry, 'notify') as notify: create_bgp_sp.return_value = payload['bgp_speaker'] self.assertEqual(self.plugin.create_bgp_speaker( test_context, test_bgp_speaker), payload['bgp_speaker']) create_bgp_sp.assert_called_once_with(test_context, test_bgp_speaker) notify.assert_called_once_with(dr_resources.BGP_SPEAKER, events.AFTER_CREATE, self.plugin, payload=payload) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/__init__.py0000664000175000017500000000000013656750631032215 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/0000775000175000017500000000000013656750704031412 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/__init__.py0000664000175000017500000000000013656750631033510 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/test_utils.py0000664000175000017500000001713213656750631034166 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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_lib import constants as lib_consts from neutron.tests import base from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.agent.driver import utils as bgp_driver_utils # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa FAKE_IP = '2.2.2.5' FAKE_IPV6 = '2001:db8::' FAKE_LOCAL_AS = 12345 FAKE_OS_KEN_SPEAKER = {} EXC_INV_PARAMTYPE = "Parameter %(param)s must be of %(param_type)s type." EXC_INV_PARAMRANGE = "%(param)s must be in %(range)s range." EXC_PASSWORD_NOTSPEC = "Password not specified for authentication " + \ "type=%(auth_type)s." EXC_INV_AUTHTYPE = "Authentication type not supported. Requested " + \ "type=%(auth_type)s." class TestValidateMethod(base.BaseTestCase): def setUp(self): super(TestValidateMethod, self).setUp() def test_validate_as_num_with_valid_as_num(self): self.assertIsNone(bgp_driver_utils.validate_as_num('local_as', 64512)) def test_validate_as_num_with_string_as_num(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': 'local_as', 'param_type': 'integer'}): bgp_driver_utils.validate_as_num('local_as', '64512') def test_validate_as_num_with_invalid_max_range(self): allowed_range = ('\\[' + str(bgp_consts.MIN_ASNUM) + '-' + str(bgp_consts.MAX_4BYTE_ASNUM) + '\\]') with self.assertRaisesRegex( bgp_driver_exc.InvalidParamRange, EXC_INV_PARAMRANGE % {'param': 'local_as', 'range': allowed_range}): bgp_driver_utils.validate_as_num('local_as', bgp_consts.MAX_4BYTE_ASNUM + 1) def test_validate_as_num_with_invalid_min_range(self): allowed_range = ('\\[' + str(bgp_consts.MIN_ASNUM) + '-' + str(bgp_consts.MAX_4BYTE_ASNUM) + '\\]') with self.assertRaisesRegex( bgp_driver_exc.InvalidParamRange, EXC_INV_PARAMRANGE % {'param': 'local_as', 'range': allowed_range}): bgp_driver_utils.validate_as_num('local_as', 0) def test_validate_auth_with_valid_auth_type(self): passwords = [None, 'password'] for (auth_type, passwd) in zip(bgp_consts.SUPPORTED_AUTH_TYPES, passwords): self.assertIsNone(bgp_driver_utils.validate_auth(auth_type, passwd)) def test_validate_auth_with_integer_password(self): auth_type = bgp_consts.SUPPORTED_AUTH_TYPES[1] with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '12345', 'param_type': 'string'}): bgp_driver_utils.validate_auth(auth_type, 12345) def test_validate_auth_with_invalid_auth_type(self): auth_type = 'abcde' with self.assertRaisesRegex( bgp_driver_exc.InvaildAuthType, EXC_INV_AUTHTYPE % {'auth_type': auth_type}): bgp_driver_utils.validate_auth(auth_type, 'password') def test_validate_auth_with_not_none_auth_type_and_none_password(self): auth_type = bgp_consts.SUPPORTED_AUTH_TYPES[1] with self.assertRaisesRegex( bgp_driver_exc.PasswordNotSpecified, EXC_PASSWORD_NOTSPEC % {'auth_type': auth_type}): bgp_driver_utils.validate_auth(auth_type, None) def test_validate_auth_with_none_auth_type_and_not_none_password(self): auth_type = None with self.assertRaisesRegex( bgp_driver_exc.InvaildAuthType, EXC_INV_AUTHTYPE % {'auth_type': auth_type}): bgp_driver_utils.validate_auth(auth_type, 'password') def test_validate_ip_addr_with_ipv4_address(self): self.assertEqual(lib_consts.IP_VERSION_4, bgp_driver_utils.validate_ip_addr(FAKE_IP)) def test_validate_ip_addr_with_ipv6_address(self): self.assertEqual(lib_consts.IP_VERSION_6, bgp_driver_utils.validate_ip_addr(FAKE_IPV6)) def test_validate_ip_addr_with_integer_ip(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '12345', 'param_type': 'ip-address'}): bgp_driver_utils.validate_ip_addr(12345) def test_validate_ip_addr_with_invalid_ipv4_type(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '1.2.3.a', 'param_type': 'ip-address'}): bgp_driver_utils.validate_ip_addr('1.2.3.a') def test_validate_ip_addr_with_invalid_ipv6_type(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '2001:db8::ggg', 'param_type': 'ip-address'}): bgp_driver_utils.validate_ip_addr('2001:db8::ggg') def test_validate_string_with_string(self): self.assertIsNone(bgp_driver_utils.validate_string(FAKE_IP)) def test_validate_string_with_integer_param(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '12345', 'param_type': 'string'}): bgp_driver_utils.validate_string(12345) class TestBgpMultiSpeakerCache(base.BaseTestCase): def setUp(self): super(TestBgpMultiSpeakerCache, self).setUp() self.expected_cache = {FAKE_LOCAL_AS: FAKE_OS_KEN_SPEAKER} self.bs_cache = bgp_driver_utils.BgpMultiSpeakerCache() def test_put_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_remove_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual(1, len(self.bs_cache.cache)) self.bs_cache.remove_bgp_speaker(FAKE_LOCAL_AS) self.assertEqual(0, len(self.bs_cache.cache)) def test_get_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual( FAKE_OS_KEN_SPEAKER, self.bs_cache.get_bgp_speaker(FAKE_LOCAL_AS)) def test_get_hosted_bgp_speakers_count(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual(1, self.bs_cache.get_hosted_bgp_speakers_count()) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/0000775000175000017500000000000013656750704032670 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/__init__.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/__init_0000664000175000017500000000000013656750631034200 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/test_driver.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/test_dr0000664000175000017500000003555413656750631034272 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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 six import mock from os_ken.services.protocols.bgp import bgpspeaker from os_ken.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_ACTIVE from oslo_config import cfg from oslo_utils import encodeutils from neutron.tests import base from neutron_dynamic_routing.services.bgp.agent import config as bgp_config from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.agent.driver.os_ken import driver as os_ken_driver # noqa # Test variables for BGP Speaker FAKE_LOCAL_AS1 = 12345 FAKE_LOCAL_AS2 = 23456 FAKE_ROUTER_ID = '1.1.1.1' # Test variables for BGP Peer FAKE_PEER_AS = 45678 FAKE_PEER_IP = '2.2.2.5' FAKE_PEER_IPV6 = '2001:db8::' FAKE_AUTH_TYPE = 'md5' FAKE_PEER_PASSWORD = 'awesome' # Test variables for Route FAKE_ROUTE = '2.2.2.0/24' FAKE_NEXTHOP = '5.5.5.5' class TestOsKenBgpDriver(base.BaseTestCase): def setUp(self): super(TestOsKenBgpDriver, self).setUp() cfg.CONF.register_opts(bgp_config.BGP_PROTO_CONFIG_OPTS, 'BGP') cfg.CONF.set_override('bgp_router_id', FAKE_ROUTER_ID, 'BGP') self.os_ken_bgp_driver = os_ken_driver.OsKenBgpDriver(cfg.CONF.BGP) mock_os_ken_speaker_p = mock.patch.object(bgpspeaker, 'BGPSpeaker') self.mock_os_ken_speaker = mock_os_ken_speaker_p.start() def test_add_new_bgp_speaker(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.mock_os_ken_speaker.assert_called_once_with( as_number=FAKE_LOCAL_AS1, router_id=FAKE_ROUTER_ID, bgp_server_port=0, best_path_change_handler=os_ken_driver.best_path_change_cb, peer_down_handler=os_ken_driver.bgp_peer_down_cb, peer_up_handler=os_ken_driver.bgp_peer_up_cb) def test_remove_bgp_speaker(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) self.os_ken_bgp_driver.delete_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(0, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.assertEqual(1, speaker.shutdown.call_count) def test_add_bgp_peer_without_password(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IP, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=None, connect_mode=CONNECT_MODE_ACTIVE) def test_add_bgp_peer_with_password(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, FAKE_AUTH_TYPE, FAKE_PEER_PASSWORD) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IP, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=encodeutils.to_utf8(FAKE_PEER_PASSWORD), connect_mode=CONNECT_MODE_ACTIVE) def test_add_bgp_peer_with_unicode_password(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) NEW_FAKE_PEER_PASSWORD = six.text_type(FAKE_PEER_PASSWORD) self.os_ken_bgp_driver.add_bgp_peer( FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, FAKE_AUTH_TYPE, NEW_FAKE_PEER_PASSWORD) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IP, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=encodeutils.to_utf8(NEW_FAKE_PEER_PASSWORD), connect_mode=CONNECT_MODE_ACTIVE) def test_add_bgp_peer_with_ipv6(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IPV6, FAKE_PEER_AS) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IPV6, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=None, connect_mode=CONNECT_MODE_ACTIVE) def test_remove_bgp_peer(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.delete_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_del.assert_called_once_with(address=FAKE_PEER_IP) def test_advertise_route(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.advertise_route(FAKE_LOCAL_AS1, FAKE_ROUTE, FAKE_NEXTHOP) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.prefix_add.assert_called_once_with(prefix=FAKE_ROUTE, next_hop=FAKE_NEXTHOP) def test_withdraw_route(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.withdraw_route(FAKE_LOCAL_AS1, FAKE_ROUTE) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.prefix_del.assert_called_once_with(prefix=FAKE_ROUTE) def test_add_same_bgp_speakers_twice(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.BgpSpeakerAlreadyScheduled, self.os_ken_bgp_driver.add_bgp_speaker, FAKE_LOCAL_AS1) def test_add_different_bgp_speakers_when_one_already_added(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.BgpSpeakerMaxScheduled, self.os_ken_bgp_driver.add_bgp_speaker, FAKE_LOCAL_AS2) def test_add_bgp_speaker_with_invalid_asnum_paramtype(self): self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_speaker, '12345') def test_add_bgp_speaker_with_invalid_asnum_range(self): self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_speaker, -1) self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_speaker, 4294967296) # valid when enables 4 byte AS number self.os_ken_bgp_driver.add_bgp_speaker(65536) def test_add_bgp_peer_with_invalid_paramtype(self): # Test with an invalid asnum data-type self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, '12345') # Test with an invalid auth-type and an invalid password self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'sha-1', 1234) # Test with an invalid auth-type and a valid password self.assertRaises(bgp_driver_exc.InvaildAuthType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'hmac-md5', FAKE_PEER_PASSWORD) # Test with none auth-type and a valid password self.assertRaises(bgp_driver_exc.InvaildAuthType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'none', FAKE_PEER_PASSWORD) # Test with none auth-type and an invalid password self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'none', 1234) # Test with a valid auth-type and no password self.assertRaises(bgp_driver_exc.PasswordNotSpecified, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, FAKE_AUTH_TYPE, None) # Test with a invalid ip address self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, '1.2.3.a', FAKE_PEER_AS, FAKE_AUTH_TYPE, FAKE_PEER_PASSWORD) def test_add_bgp_peer_with_invalid_asnum_range(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, -1) self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, 4294967296) # valid when enables 4 byte AS number self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, 65536) def test_add_bgp_peer_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS) def test_remove_bgp_peer_with_invalid_paramtype(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.delete_bgp_peer, FAKE_LOCAL_AS1, 12345) def test_remove_bgp_peer_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.delete_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP) def test_advertise_route_with_invalid_paramtype(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.advertise_route, FAKE_LOCAL_AS1, 12345, FAKE_NEXTHOP) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.advertise_route, FAKE_LOCAL_AS1, FAKE_ROUTE, 12345) def test_advertise_route_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.advertise_route, FAKE_LOCAL_AS1, FAKE_ROUTE, FAKE_NEXTHOP) def test_withdraw_route_with_invalid_paramtype(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.withdraw_route, FAKE_LOCAL_AS1, 12345) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.withdraw_route, FAKE_LOCAL_AS1, 12345) def test_withdraw_route_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.withdraw_route, FAKE_LOCAL_AS1, FAKE_ROUTE) def test_add_multiple_bgp_speakers(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.assertRaises(bgp_driver_exc.BgpSpeakerMaxScheduled, self.os_ken_bgp_driver.add_bgp_speaker, FAKE_LOCAL_AS2) self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.delete_bgp_speaker, FAKE_LOCAL_AS2) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.delete_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(0, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/0000775000175000017500000000000013656750704031215 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/test_bgp_dragent.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/test_bgp_dragen0000664000175000017500000010255713656750631034300 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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 copy import sys import eventlet import mock from neutron_lib import context from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.common import config as n_config from neutron.conf.agent import common as config from neutron.tests import base from neutron_dynamic_routing.services.bgp.agent import bgp_dragent from neutron_dynamic_routing.services.bgp.agent import config as bgp_config HOSTNAME = 'hostname' rpc_api = bgp_dragent.BgpDrPluginApi BGP_PLUGIN = '%s.%s' % (rpc_api.__module__, rpc_api.__name__) FAKE_BGPSPEAKER_UUID = uuidutils.generate_uuid() FAKE_BGPPEER_UUID = uuidutils.generate_uuid() FAKE_BGP_SPEAKER = {'id': FAKE_BGPSPEAKER_UUID, 'local_as': 12345, 'peers': [{'remote_as': '2345', 'peer_ip': '1.1.1.1', 'auth_type': 'none', 'password': ''}], 'advertised_routes': []} FAKE_BGP_PEER = {'id': FAKE_BGPPEER_UUID, 'remote_as': '2345', 'peer_ip': '1.1.1.1', 'auth_type': 'none', 'password': ''} FAKE_ROUTE = {'id': FAKE_BGPSPEAKER_UUID, 'destination': '2.2.2.2/32', 'next_hop': '3.3.3.3'} FAKE_ROUTES = {'routes': {'id': FAKE_BGPSPEAKER_UUID, 'destination': '2.2.2.2/32', 'next_hop': '3.3.3.3'} } class TestBgpDrAgent(base.BaseTestCase): def setUp(self): super(TestBgpDrAgent, self).setUp() cfg.CONF.register_opts(bgp_config.BGP_DRIVER_OPTS, 'BGP') cfg.CONF.register_opts(bgp_config.BGP_PROTO_CONFIG_OPTS, 'BGP') cfg.CONF.register_opts(config.AGENT_STATE_OPTS, 'AGENT') mock_log_p = mock.patch.object(bgp_dragent, 'LOG') self.mock_log = mock_log_p.start() self.driver_cls_p = mock.patch( 'neutron_dynamic_routing.services.bgp.agent.bgp_dragent.' 'importutils.import_class') self.driver_cls = self.driver_cls_p.start() self.context = context.get_admin_context() @mock.patch('neutron.common.config.init') def test_bgp_dragent_manager(self, mock_init): mock_init.return_value = '/tmp/test' state_rpc_str = 'neutron.agent.rpc.PluginReportStateAPI' # sync_state is needed for this test with mock.patch.object(bgp_dragent.BgpDrAgentWithStateReport, 'sync_state', autospec=True) as mock_sync_state: with mock.patch(state_rpc_str) as state_rpc: test_args = [ 'bgp_dragent', '--config-file', base.etcdir('neutron.conf') ] with mock.patch.object(sys, 'argv', test_args): config.register_agent_state_opts_helper(cfg.CONF) n_config.init(sys.argv[1:]) agent_mgr = bgp_dragent.BgpDrAgentWithStateReport( 'testhost') eventlet.greenthread.sleep(1) agent_mgr.after_start() self.assertIsNotNone(len(mock_sync_state.mock_calls)) state_rpc.assert_has_calls( [mock.call(mock.ANY), mock.call().report_state(mock.ANY, mock.ANY, mock.ANY)]) def test_run_completes_single_pass(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, 'sync_state') as sync_state: bgp_dr.run() self.assertIsNotNone(len(sync_state.mock_calls)) def test_after_start(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, 'sync_state') as sync_state: bgp_dr.after_start() self.assertIsNotNone(len(sync_state.mock_calls)) def test_agent_updated(self): bgp_dr = bgp_dragent.BgpDrAgentWithStateReport(HOSTNAME) payload = {'admin_state_up': True} with mock.patch.object(bgp_dr, 'agent_updated') as agent_updated: bgp_dr.agent_updated(self.context, payload) self.assertIsNotNone(len(agent_updated.mock_calls)) self.assertEqual(1, bgp_dr.agent_updated.call_count) def _test_sync_state_helper(self, bgp_speaker_list=None, cached_info=None, safe_configure_call_count=0, sync_bgp_speaker_call_count=0, remove_bgp_speaker_call_count=0, remove_bgp_speaker_ids=None, added_bgp_speakers=None, synced_bgp_speakers=None): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) attrs_to_mock = dict( [(a, mock.Mock()) for a in ['plugin_rpc', 'sync_bgp_speaker', 'safe_configure_dragent_for_bgp_speaker', 'remove_bgp_speaker_from_dragent']]) with mock.patch.multiple(bgp_dr, **attrs_to_mock): if not cached_info: cached_info = {} if not added_bgp_speakers: added_bgp_speakers = [] if not remove_bgp_speaker_ids: remove_bgp_speaker_ids = [] if not synced_bgp_speakers: synced_bgp_speakers = [] bgp_dr.plugin_rpc.get_bgp_speakers.return_value = bgp_speaker_list bgp_dr.cache.cache = cached_info bgp_dr.cache.clear_cache = mock.Mock() bgp_dr.sync_state(mock.ANY) self.assertEqual( remove_bgp_speaker_call_count, bgp_dr.remove_bgp_speaker_from_dragent.call_count) if remove_bgp_speaker_call_count: expected_calls = [mock.call(bgp_speaker_id) for bgp_speaker_id in remove_bgp_speaker_ids] bgp_dr.remove_bgp_speaker_from_dragent.assert_has_calls( expected_calls) self.assertEqual( safe_configure_call_count, bgp_dr.safe_configure_dragent_for_bgp_speaker.call_count) if safe_configure_call_count: expected_calls = [mock.call(bgp_speaker) for bgp_speaker in added_bgp_speakers] bgp_dr.safe_configure_dragent_for_bgp_speaker.assert_has_calls( expected_calls) self.assertEqual(sync_bgp_speaker_call_count, bgp_dr.sync_bgp_speaker.call_count) if sync_bgp_speaker_call_count: expected_calls = [mock.call(bgp_speaker) for bgp_speaker in synced_bgp_speakers] bgp_dr.sync_bgp_speaker.assert_has_calls(expected_calls) def test_sync_state_bgp_speaker_added(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}] self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, safe_configure_call_count=1, added_bgp_speakers=bgp_speaker_list) def test_sync_state_bgp_speaker_deleted(self): bgp_speaker_list = [] cached_bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': ['peer-1'], 'advertised_routes': []} cached_info = {'foo-id': cached_bgp_speaker} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, remove_bgp_speaker_call_count=1, remove_bgp_speaker_ids=['foo-id']) def test_sync_state_added_and_deleted(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}] cached_bgp_speaker = {'bgp_speaker': {'local_as': 12345}, 'peers': ['peer-1'], 'advertised_routes': []} cached_info = {'bar-id': cached_bgp_speaker} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, remove_bgp_speaker_call_count=1, remove_bgp_speaker_ids=['bar-id'], safe_configure_call_count=1, added_bgp_speakers=bgp_speaker_list) def test_sync_state_added_and_synced(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}, {'id': 'bar-id', 'peers': ['peer-2'], 'advertised_routes': []}, {'id': 'temp-id', 'peers': ['temp-1'], 'advertised_routes': []}] cached_bgp_speaker = {'id': 'bar-id', 'bgp_speaker': {'id': 'bar-id'}, 'peers': ['peer-1'], 'advertised_routes': []} cached_bgp_speaker_2 = {'id': 'temp-id', 'bgp_speaker': {'id': 'temp-id'}, 'peers': ['temp-1'], 'advertised_routes': []} cached_info = {'bar-id': cached_bgp_speaker, 'temp-id': cached_bgp_speaker_2} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, safe_configure_call_count=1, added_bgp_speakers=[bgp_speaker_list[0]], sync_bgp_speaker_call_count=2, synced_bgp_speakers=[bgp_speaker_list[1], bgp_speaker_list[2]] ) def test_sync_state_added_synced_and_removed(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}, {'id': 'bar-id', 'peers': ['peer-2'], 'advertised_routes': []}] cached_bgp_speaker = {'id': 'bar-id', 'bgp_speaker': {'id': 'bar-id'}, 'peers': ['peer-1'], 'advertised_routes': []} cached_bgp_speaker_2 = {'id': 'temp-id', 'bgp_speaker': {'id': 'temp-id'}, 'peers': ['temp-1'], 'advertised_routes': []} cached_info = {'bar-id': cached_bgp_speaker, 'temp-id': cached_bgp_speaker_2} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, remove_bgp_speaker_call_count=1, remove_bgp_speaker_ids=['temp-id'], safe_configure_call_count=1, added_bgp_speakers=[bgp_speaker_list[0]], sync_bgp_speaker_call_count=1, synced_bgp_speakers=[bgp_speaker_list[1]]) def _test_sync_bgp_speaker_helper(self, bgp_speaker, cached_info=None, remove_bgp_peer_call_count=0, removed_bgp_peer_ip_list=None, withdraw_route_call_count=0, withdraw_routes_list=None, add_bgp_peers_called=False, advertise_routes_called=False): if not cached_info: cached_info = {} if not removed_bgp_peer_ip_list: removed_bgp_peer_ip_list = [] if not withdraw_routes_list: withdraw_routes_list = [] bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) attrs_to_mock = dict( [(a, mock.Mock()) for a in ['remove_bgp_peer_from_bgp_speaker', 'add_bgp_peers_to_bgp_speaker', 'advertise_routes_via_bgp_speaker', 'withdraw_route_via_bgp_speaker']]) with mock.patch.multiple(bgp_dr, **attrs_to_mock): bgp_dr.cache.cache = cached_info bgp_dr.sync_bgp_speaker(bgp_speaker) self.assertEqual( remove_bgp_peer_call_count, bgp_dr.remove_bgp_peer_from_bgp_speaker.call_count) if remove_bgp_peer_call_count: expected_calls = [mock.call(bgp_speaker['id'], peer_ip) for peer_ip in removed_bgp_peer_ip_list] bgp_dr.remove_bgp_peer_from_bgp_speaker.assert_has_calls( expected_calls) self.assertEqual(add_bgp_peers_called, bgp_dr.add_bgp_peers_to_bgp_speaker.called) if add_bgp_peers_called: bgp_dr.add_bgp_peers_to_bgp_speaker.assert_called_with( bgp_speaker) self.assertEqual( withdraw_route_call_count, bgp_dr.withdraw_route_via_bgp_speaker.call_count) if withdraw_route_call_count: expected_calls = [mock.call(bgp_speaker['id'], 12345, route) for route in withdraw_routes_list] bgp_dr.withdraw_route_via_bgp_speaker.assert_has_calls( expected_calls) self.assertEqual(advertise_routes_called, bgp_dr.advertise_routes_via_bgp_speaker.called) if advertise_routes_called: bgp_dr.advertise_routes_via_bgp_speaker.assert_called_with( bgp_speaker) def test_sync_bgp_speaker_bgp_peers_updated(self): peers = [{'id': 'peer-1', 'peer_ip': '1.1.1.1'}, {'id': 'peer-2', 'peer_ip': '2.2.2.2'}] bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': peers, 'advertised_routes': []} cached_peers = {'1.1.1.1': {'id': 'peer-2', 'peer_ip': '1.1.1.1'}, '3.3.3.3': {'id': 'peer-3', 'peer_ip': '3.3.3.3'}} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': cached_peers, 'advertised_routes': []}} self._test_sync_bgp_speaker_helper( bgp_speaker, cached_info=cached_bgp_speaker, remove_bgp_peer_call_count=1, removed_bgp_peer_ip_list=['3.3.3.3'], add_bgp_peers_called=True, advertise_routes_called=False) def test_sync_bgp_speaker_routes_updated(self): adv_routes = [{'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'}, {'destination': '20.0.0.0/24', 'next_hop': '2.2.2.2'}] bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': {}, 'advertised_routes': adv_routes} cached_adv_routes = [{'destination': '20.0.0.0/24', 'next_hop': '2.2.2.2'}, {'destination': '30.0.0.0/24', 'next_hop': '3.3.3.3'}] cached_bgp_speaker = { 'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': cached_adv_routes}} self._test_sync_bgp_speaker_helper( bgp_speaker, cached_info=cached_bgp_speaker, withdraw_route_call_count=1, withdraw_routes_list=[cached_adv_routes[1]], add_bgp_peers_called=False, advertise_routes_called=True) def test_sync_bgp_speaker_peers_routes_added(self): peers = [{'id': 'peer-1', 'peer_ip': '1.1.1.1'}, {'id': 'peer-2', 'peer_ip': '2.2.2.2'}] adv_routes = [{'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'}, {'destination': '20.0.0.0/24', 'next_hop': '2.2.2.2'}] bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': peers, 'advertised_routes': adv_routes} cached_bgp_speaker = { 'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': []}} self._test_sync_bgp_speaker_helper( bgp_speaker, cached_info=cached_bgp_speaker, add_bgp_peers_called=True, advertise_routes_called=True) def test_sync_state_plugin_error(self): with mock.patch(BGP_PLUGIN) as plug: mock_plugin = mock.Mock() mock_plugin.get_bgp_speakers.side_effect = Exception plug.return_value = mock_plugin with mock.patch.object(bgp_dragent.LOG, 'error') as log: bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, 'schedule_full_resync') as schedule_full_resync: bgp_dr.sync_state(mock.ANY) self.assertTrue(log.called) self.assertTrue(schedule_full_resync.called) def test_periodic_resync(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, '_periodic_resync_helper') as resync_helper: bgp_dr.periodic_resync(self.context) self.assertTrue(resync_helper.called) def test_periodic_resync_helper(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) bgp_dr.schedule_resync('foo reason', 'foo-id') with mock.patch.object(bgp_dr, 'sync_state') as sync_state: sync_state.side_effect = RuntimeError with testtools.ExpectedException(RuntimeError): bgp_dr._periodic_resync_helper(self.context) self.assertTrue(sync_state.called) self.assertEqual(len(bgp_dr.needs_resync_reasons), 0) def _test_add_bgp_peer_helper(self, bgp_speaker_id, bgp_peer, cached_bgp_speaker, put_bgp_peer_called=True): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) bgp_dr.cache.cache = cached_bgp_speaker with mock.patch.object( bgp_dr.cache, 'put_bgp_peer') as mock_put_bgp_peer: bgp_dr.add_bgp_peer_to_bgp_speaker('foo-id', 12345, bgp_peer) if put_bgp_peer_called: mock_put_bgp_peer.assert_called_once_with( bgp_speaker_id, bgp_peer) else: self.assertFalse(mock_put_bgp_peer.called) def test_add_bgp_peer_not_cached(self): bgp_peer = {'peer_ip': '1.1.1.1', 'remote_as': 34567, 'auth_type': 'md5', 'password': 'abc'} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': []}} self._test_add_bgp_peer_helper('foo-id', bgp_peer, cached_bgp_speaker) def test_add_bgp_peer_already_cached(self): bgp_peer = {'peer_ip': '1.1.1.1', 'remote_as': 34567, 'auth_type': 'md5', 'password': 'abc'} cached_peers = {'1.1.1.1': {'peer_ip': '1.1.1.1', 'remote_as': 34567}} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': cached_peers, 'advertised_routes': []}} self._test_add_bgp_peer_helper('foo-id', bgp_peer, cached_bgp_speaker, put_bgp_peer_called=False) def _test_advertise_route_helper(self, bgp_speaker_id, route, cached_bgp_speaker, put_adv_route_called=True): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) bgp_dr.cache.cache = cached_bgp_speaker with mock.patch.object( bgp_dr.cache, 'put_adv_route') as mock_put_adv_route: bgp_dr.advertise_route_via_bgp_speaker(bgp_speaker_id, 12345, route) if put_adv_route_called: mock_put_adv_route.assert_called_once_with( bgp_speaker_id, route) else: self.assertFalse(mock_put_adv_route.called) def test_advertise_route_helper_not_cached(self): route = {'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': []}} self._test_advertise_route_helper('foo-id', route, cached_bgp_speaker, put_adv_route_called=True) def test_advertise_route_helper_already_cached(self): route = {'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': [route]}} self._test_advertise_route_helper('foo-id', route, cached_bgp_speaker, put_adv_route_called=False) class TestBgpDrAgentEventHandler(base.BaseTestCase): cache_cls = 'neutron_dynamic_routing.services.bgp.'\ 'agent.bgp_dragent.BgpSpeakerCache' def setUp(self): super(TestBgpDrAgentEventHandler, self).setUp() cfg.CONF.register_opts(bgp_config.BGP_DRIVER_OPTS, 'BGP') cfg.CONF.register_opts(bgp_config.BGP_PROTO_CONFIG_OPTS, 'BGP') mock_log_p = mock.patch.object(bgp_dragent, 'LOG') self.mock_log = mock_log_p.start() self.plugin_p = mock.patch(BGP_PLUGIN) plugin_cls = self.plugin_p.start() self.plugin = mock.Mock() plugin_cls.return_value = self.plugin self.cache_p = mock.patch(self.cache_cls) cache_cls = self.cache_p.start() self.cache = mock.Mock() cache_cls.return_value = self.cache self.driver_cls_p = mock.patch( 'neutron_dynamic_routing.services.bgp.agent.bgp_dragent.' 'importutils.import_class') self.driver_cls = self.driver_cls_p.start() self.bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) self.schedule_full_resync_p = mock.patch.object( self.bgp_dr, 'schedule_full_resync') self.schedule_full_resync = self.schedule_full_resync_p.start() self.context = mock.Mock() def test_bgp_speaker_create_end(self): payload = {'bgp_speaker': {'id': FAKE_BGPSPEAKER_UUID}} with mock.patch.object(self.bgp_dr, 'add_bgp_speaker_helper') as enable: self.bgp_dr.bgp_speaker_create_end(None, payload) enable.assert_called_once_with(FAKE_BGP_SPEAKER['id']) def test_bgp_peer_association_end(self): payload = {'bgp_peer': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'peer_id': FAKE_BGPPEER_UUID}} with mock.patch.object(self.bgp_dr, 'add_bgp_peer_helper') as enable: self.bgp_dr.bgp_peer_association_end(None, payload) enable.assert_called_once_with(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER['id']) def test_route_advertisement_end(self): routes = [{'destination': '2.2.2.2/32', 'next_hop': '3.3.3.3'}, {'destination': '4.4.4.4/32', 'next_hop': '5.5.5.5'}] payload = {'advertise_routes': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'routes': routes}} expected_calls = [mock.call(FAKE_BGP_SPEAKER['id'], routes)] with mock.patch.object(self.bgp_dr, 'add_routes_helper') as enable: self.bgp_dr.bgp_routes_advertisement_end(None, payload) enable.assert_has_calls(expected_calls) def test_add_bgp_speaker_helper(self): self.plugin.get_bgp_speaker_info.return_value = FAKE_BGP_SPEAKER add_bs_p = mock.patch.object(self.bgp_dr, 'add_bgp_speaker_on_dragent') add_bs = add_bs_p.start() self.bgp_dr.add_bgp_speaker_helper(FAKE_BGP_SPEAKER['id']) self.plugin.assert_has_calls([ mock.call.get_bgp_speaker_info(mock.ANY, FAKE_BGP_SPEAKER['id'])]) add_bs.assert_called_once_with(FAKE_BGP_SPEAKER) def test_add_bgp_peer_helper(self): self.plugin.get_bgp_peer_info.return_value = FAKE_BGP_PEER add_bp_p = mock.patch.object(self.bgp_dr, 'add_bgp_peer_to_bgp_speaker') add_bp = add_bp_p.start() self.bgp_dr.add_bgp_peer_helper(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER['id']) self.plugin.assert_has_calls([ mock.call.get_bgp_peer_info(mock.ANY, FAKE_BGP_PEER['id'])]) self.assertEqual(1, add_bp.call_count) def test_add_routes_helper(self): add_rt_p = mock.patch.object(self.bgp_dr, 'advertise_route_via_bgp_speaker') add_bp = add_rt_p.start() self.bgp_dr.add_routes_helper(FAKE_BGP_SPEAKER['id'], FAKE_ROUTES) self.assertEqual(1, add_bp.call_count) def test_bgp_speaker_remove_end(self): payload = {'bgp_speaker': {'id': FAKE_BGPSPEAKER_UUID}} with mock.patch.object(self.bgp_dr, 'remove_bgp_speaker_from_dragent') as disable: self.bgp_dr.bgp_speaker_remove_end(None, payload) disable.assert_called_once_with(FAKE_BGP_SPEAKER['id']) def test_bgp_peer_disassociation_end(self): payload = {'bgp_peer': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'peer_ip': '1.1.1.1'}} with mock.patch.object(self.bgp_dr, 'remove_bgp_peer_from_bgp_speaker') as disable: self.bgp_dr.bgp_peer_disassociation_end(None, payload) disable.assert_called_once_with(FAKE_BGPSPEAKER_UUID, FAKE_BGP_PEER['peer_ip']) def test_bgp_routes_withdrawal_end(self): withdraw_routes = [{'destination': '2.2.2.2/32'}, {'destination': '3.3.3.3/32'}] payload = {'withdraw_routes': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'routes': withdraw_routes}} expected_calls = [mock.call(FAKE_BGP_SPEAKER['id'], withdraw_routes)] with mock.patch.object(self.bgp_dr, 'withdraw_routes_helper') as disable: self.bgp_dr.bgp_routes_withdrawal_end(None, payload) disable.assert_has_calls(expected_calls) class TestBGPSpeakerCache(base.BaseTestCase): def setUp(self): super(TestBGPSpeakerCache, self).setUp() self.expected_cache = {FAKE_BGP_SPEAKER['id']: {'bgp_speaker': FAKE_BGP_SPEAKER, 'peers': {}, 'advertised_routes': []}} self.bs_cache = bgp_dragent.BgpSpeakerCache() def test_put_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_put_bgp_speaker_existing(self): prev_bs_info = {'id': 'foo-id'} with mock.patch.object(self.bs_cache, 'remove_bgp_speaker_by_id') as remove: self.bs_cache.cache[FAKE_BGP_SPEAKER['id']] = prev_bs_info self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) remove.assert_called_once_with(prev_bs_info) self.assertEqual(self.expected_cache, self.bs_cache.cache) def remove_bgp_speaker_by_id(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual(1, len(self.bs_cache.cache)) self.bs_cache.remove_bgp_speaker_by_id(FAKE_BGP_SPEAKER['id']) self.assertEqual(0, len(self.bs_cache.cache)) def test_get_bgp_speaker_by_id(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual( FAKE_BGP_SPEAKER, self.bs_cache.get_bgp_speaker_by_id(FAKE_BGP_SPEAKER['id'])) def test_get_bgp_speaker_ids(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual([FAKE_BGP_SPEAKER['id']], list(self.bs_cache.get_bgp_speaker_ids())) def _test_bgp_peer_helper(self, remove=False): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.bs_cache.put_bgp_peer(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER) expected_cache = copy.deepcopy(self.expected_cache) expected_cache[FAKE_BGP_SPEAKER['id']]['peers'] = { FAKE_BGP_PEER['peer_ip']: FAKE_BGP_PEER} self.assertEqual(expected_cache, self.bs_cache.cache) if remove: self.bs_cache.remove_bgp_peer_by_ip(FAKE_BGP_SPEAKER['id'], 'foo-ip') self.assertEqual(expected_cache, self.bs_cache.cache) self.bs_cache.remove_bgp_peer_by_ip(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER['peer_ip']) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_put_bgp_peer(self): self._test_bgp_peer_helper() def test_remove_bgp_peer(self): self._test_bgp_peer_helper(remove=True) def _test_bgp_speaker_adv_route_helper(self, remove=False): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.bs_cache.put_adv_route(FAKE_BGP_SPEAKER['id'], FAKE_ROUTE) expected_cache = copy.deepcopy(self.expected_cache) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'].append( FAKE_ROUTE) self.assertEqual(expected_cache, self.bs_cache.cache) fake_route_2 = copy.deepcopy(FAKE_ROUTE) fake_route_2['destination'] = '4.4.4.4/32' self.bs_cache.put_adv_route(FAKE_BGP_SPEAKER['id'], fake_route_2) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'].append( fake_route_2) self.assertEqual(expected_cache, self.bs_cache.cache) if remove: self.bs_cache.remove_adv_route(FAKE_BGP_SPEAKER['id'], fake_route_2) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'] = ( [FAKE_ROUTE]) self.assertEqual(expected_cache, self.bs_cache.cache) self.bs_cache.remove_adv_route(FAKE_BGP_SPEAKER['id'], FAKE_ROUTE) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_put_bgp_speaker_adv_route(self): self._test_bgp_speaker_adv_route_helper() def test_remove_bgp_speaker_adv_route(self): self._test_bgp_speaker_adv_route_helper(remove=True) def test_is_bgp_speaker_adv_route_present(self): self._test_bgp_speaker_adv_route_helper() self.assertTrue(self.bs_cache.is_route_advertised( FAKE_BGP_SPEAKER['id'], FAKE_ROUTE)) self.assertFalse(self.bs_cache.is_route_advertised( FAKE_BGP_SPEAKER['id'], {'destination': 'foo-destination', 'next_hop': 'foo-next-hop'})) def test_put_bgp_speaker_adv_route_and_remove_with_next_hop_none(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.bs_cache.put_adv_route(FAKE_BGP_SPEAKER['id'], FAKE_ROUTE) expected_cache = copy.deepcopy(self.expected_cache) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'].append( FAKE_ROUTE) self.assertEqual(expected_cache, self.bs_cache.cache) fake_route_2 = copy.deepcopy(FAKE_ROUTE) fake_route_2.update({'next_hop': None}) self.assertTrue(self.bs_cache.is_route_advertised( FAKE_BGP_SPEAKER['id'], fake_route_2)) self.bs_cache.remove_adv_route(FAKE_BGP_SPEAKER['id'], fake_route_2) self.assertEqual(self.expected_cache, self.bs_cache.cache) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/__init__.py0000664000175000017500000000000013656750631033313 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/0000775000175000017500000000000013656750704032075 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/__init__.py0000664000175000017500000000000013656750631034173 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/test_bgp_dragent_scheduler.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/test_bgp_dr0000664000175000017500000003502713656750631034322 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock import testscenarios from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context from oslo_utils import importutils from neutron.tests.unit import testlib_api from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.services.bgp import bgp_plugin from neutron_dynamic_routing.services.bgp.scheduler import bgp_dragent_scheduler as bgp_dras # noqa from neutron_dynamic_routing.tests.common import helpers # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios class TestBgpDrAgentSchedulerBaseTestCase(testlib_api.SqlTestCase): def setUp(self): super(TestBgpDrAgentSchedulerBaseTestCase, self).setUp() self.ctx = context.get_admin_context() self.bgp_speaker = {'id': 'foo_bgp_speaker_id'} self.bgp_speaker_id = 'foo_bgp_speaker_id' self._save_bgp_speaker(self.bgp_speaker_id) def _create_and_set_agents_down(self, hosts, down_agent_count=0, admin_state_up=True): agents = [] for i, host in enumerate(hosts): is_alive = i >= down_agent_count agents.append(helpers.register_bgp_dragent( host, admin_state_up=admin_state_up, alive=is_alive)) return agents def _save_bgp_speaker(self, bgp_speaker_id): cls = bgp_db.BgpDbMixin() bgp_speaker_body = {'bgp_speaker': { 'name': 'fake_bgp_speaker', 'ip_version': '4', 'local_as': '123', 'advertise_floating_ip_host_routes': False, 'advertise_tenant_networks': False, 'peers': [], 'networks': []}} cls._save_bgp_speaker(self.ctx, bgp_speaker_body, uuid=bgp_speaker_id) def _get_dragent_bgp_speaker_bindings(self, bgp_speaker_id): return self.ctx.session.query( bgp_dras_db.BgpSpeakerDrAgentBinding).filter_by( bgp_speaker_id=bgp_speaker_id).all() def _test_schedule_bind_bgp_speaker(self, agents, bgp_speaker_id): scheduler = bgp_dras.ChanceScheduler() scheduler.resource_filter.bind(self.ctx, agents, bgp_speaker_id) results = self._get_dragent_bgp_speaker_bindings(bgp_speaker_id) for result in results: self.assertEqual(bgp_speaker_id, result.bgp_speaker_id) class TestSchedulerCallback(TestBgpDrAgentSchedulerBaseTestCase): def setUp(self): super(TestSchedulerCallback, self).setUp() bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.' 'agentnotifiers.bgp_dr_rpc_agent_api.' 'BgpDrAgentNotifyApi') bgp_notify_p.start() rpc_conn_p = mock.patch('neutron_lib.rpc.Connection') rpc_conn_p.start() self.plugin = bgp_plugin.BgpPlugin() self.scheduler = bgp_dras.ChanceScheduler() def _create_test_payload(self, context='test_ctx'): bgp_speaker = {'id': '11111111-2222-3333-4444-555555555555'} payload = {'plugin': self.plugin, 'context': context, 'bgp_speaker': bgp_speaker} return payload def test__register_callbacks(self): with mock.patch.object(registry, 'subscribe') as subscribe: scheduler = bgp_dras.ChanceScheduler() expected_calls = [ mock.call(scheduler.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE), ] self.assertEqual(subscribe.call_args_list, expected_calls) with mock.patch.object(registry, 'subscribe') as subscribe: scheduler = bgp_dras.WeightScheduler() expected_calls = [ mock.call(scheduler.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE), ] self.assertEqual(subscribe.call_args_list, expected_calls) def test_schedule_bgp_speaker_callback_with_valid_event(self): payload = self._create_test_payload() with mock.patch.object(self.plugin, 'schedule_bgp_speaker') as sched_bgp: self.scheduler.schedule_bgp_speaker_callback( dr_resources.BGP_SPEAKER, events.AFTER_CREATE, self.scheduler, payload) sched_bgp.assert_called_once_with(mock.ANY, payload['bgp_speaker']) def test_schedule_bgp_speaker_callback_with_invalid_event(self): payload = self._create_test_payload() with mock.patch.object(self.plugin, 'schedule_bgp_speaker') as sched_bgp: self.scheduler.schedule_bgp_speaker_callback( dr_resources.BGP_SPEAKER, events.BEFORE_CREATE, self.scheduler, payload) sched_bgp.assert_not_called() class TestBgpDrAgentScheduler(TestBgpDrAgentSchedulerBaseTestCase, bgp_db.BgpDbMixin): def test_schedule_bind_bgp_speaker_single_agent(self): agents = self._create_and_set_agents_down(['host-a']) self._test_schedule_bind_bgp_speaker(agents, self.bgp_speaker_id) def test_schedule_bind_bgp_speaker_multi_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_bgp_speaker(agents, self.bgp_speaker_id) class TestBgpAgentFilter(TestBgpDrAgentSchedulerBaseTestCase, bgp_db.BgpDbMixin, bgp_dras_db.BgpDrAgentSchedulerDbMixin): def setUp(self): super(TestBgpAgentFilter, self).setUp() self.bgp_drscheduler = importutils.import_object( 'neutron_dynamic_routing.services.bgp.scheduler.' 'bgp_dragent_scheduler.ChanceScheduler' ) self.plugin = self def _test_filter_agents_helper(self, bgp_speaker, expected_filtered_dragent_ids=None, expected_num_agents=1): filtered_agents = ( self.plugin.bgp_drscheduler.resource_filter.filter_agents( self.plugin, self.ctx, bgp_speaker)) self.assertEqual(expected_num_agents, filtered_agents['n_agents']) actual_filtered_dragent_ids = [ agent.id for agent in filtered_agents['hostable_agents']] if expected_filtered_dragent_ids is None: expected_filtered_dragent_ids = [] self.assertEqual(len(expected_filtered_dragent_ids), len(actual_filtered_dragent_ids)) for filtered_agent_id in actual_filtered_dragent_ids: self.assertIn(filtered_agent_id, expected_filtered_dragent_ids) def test_filter_agents_single_agent(self): agents = self._create_and_set_agents_down(['host-a']) expected_filtered_dragent_ids = [agents[0].id] self._test_filter_agents_helper( self.bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids) def test_filter_agents_no_agents(self): expected_filtered_dragent_ids = [] self._test_filter_agents_helper( self.bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids, expected_num_agents=0) def test_filter_agents_two_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) expected_filtered_dragent_ids = [agent.id for agent in agents] self._test_filter_agents_helper( self.bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids) def test_filter_agents_agent_already_scheduled(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._test_filter_agents_helper(self.bgp_speaker, expected_num_agents=0) def test_filter_agents_multiple_agents_bgp_speakers(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) bgp_speaker = {'id': 'bar-speaker-id'} self._save_bgp_speaker(bgp_speaker['id']) expected_filtered_dragent_ids = [agents[1].id] self._test_filter_agents_helper( bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids) class TestAutoScheduleBgpSpeakers(TestBgpDrAgentSchedulerBaseTestCase): """Unit test scenarios for schedule_unscheduled_bgp_speakers. bgp_speaker_present BGP speaker is present or not scheduled_already BGP speaker is already scheduled to the agent or not agent_down BGP DRAgent is down or alive valid_host If true, then an valid host is passed to schedule BGP speaker, else an invalid host is passed. """ scenarios = [ ('BGP speaker present', dict(bgp_speaker_present=True, scheduled_already=False, agent_down=False, valid_host=True, expected_result=True)), ('No BGP speaker', dict(bgp_speaker_present=False, scheduled_already=False, agent_down=False, valid_host=True, expected_result=False)), ('BGP speaker already scheduled', dict(bgp_speaker_present=True, scheduled_already=True, agent_down=False, valid_host=True, expected_result=False)), ('BGP DR agent down', dict(bgp_speaker_present=True, scheduled_already=False, agent_down=True, valid_host=False, expected_result=False)), ('Invalid host', dict(bgp_speaker_present=True, scheduled_already=False, agent_down=False, valid_host=False, expected_result=False)), ] def test_auto_schedule_bgp_speaker(self): scheduler = bgp_dras.ChanceScheduler() if self.bgp_speaker_present: down_agent_count = 1 if self.agent_down else 0 agents = self._create_and_set_agents_down( ['host-a'], down_agent_count=down_agent_count) if self.scheduled_already: self._test_schedule_bind_bgp_speaker(agents, self.bgp_speaker_id) expected_hosted_agents = (1 if self.bgp_speaker_present and self.valid_host else 0) host = "host-a" if self.valid_host else "host-b" observed_ret_value = scheduler.schedule_unscheduled_bgp_speakers( self.ctx, host) self.assertEqual(self.expected_result, observed_ret_value) hosted_agents = self.ctx.session.query( bgp_dras_db.BgpSpeakerDrAgentBinding).all() self.assertEqual(expected_hosted_agents, len(hosted_agents)) class TestRescheduleBgpSpeaker(TestBgpDrAgentSchedulerBaseTestCase, bgp_db.BgpDbMixin): def setUp(self): super(TestRescheduleBgpSpeaker, self).setUp() bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.' 'agentnotifiers.bgp_dr_rpc_agent_api.' 'BgpDrAgentNotifyApi') bgp_notify_p.start() rpc_conn_p = mock.patch('neutron_lib.rpc.Connection') rpc_conn_p.start() self.plugin = bgp_plugin.BgpPlugin() self.scheduler = bgp_dras.ChanceScheduler() self.host1 = 'host-a' self.host2 = 'host-b' def _kill_bgp_dragent(self, hosts): agents = [] for host in hosts: agents.append( helpers.register_bgp_dragent(host=host, alive=False)) return agents def _schedule_bind_bgp_speaker(self, agents, bgp_speaker_id): self.scheduler.resource_filter.bind(self.ctx, agents, bgp_speaker_id) return self._get_dragent_bgp_speaker_bindings(bgp_speaker_id) def test_reschedule_bgp_speaker_bound_to_down_dragent(self): agents = self._create_and_set_agents_down([self.host1, self.host2]) self._schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._kill_bgp_dragent([self.host1]) self.plugin.remove_bgp_speaker_from_down_dragents() binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds[0].agent_id, agents[1].id) def test_no_schedule_with_non_available_dragent(self): agents = self._create_and_set_agents_down([self.host1, self.host2]) self._schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._kill_bgp_dragent([self.host1, self.host2]) self.plugin.remove_bgp_speaker_from_down_dragents() binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds, []) def test_schedule_unbind_bgp_speaker(self): agents = self._create_and_set_agents_down([self.host1, self.host2]) self._schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._kill_bgp_dragent([self.host1, self.host2]) self.plugin.remove_bgp_speaker_from_down_dragents() binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds, []) # schedule a unbind bgp speaker agents = self._create_and_set_agents_down([self.host1]) self.scheduler.schedule_all_unscheduled_bgp_speakers(self.ctx) binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds[0].agent_id, agents[0].id) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/0000775000175000017500000000000013656750704026275 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/__init__.py0000664000175000017500000000000013656750631030373 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/0000775000175000017500000000000013656750704027061 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/__init__.py0000664000175000017500000000000013656750631031157 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/0000775000175000017500000000000013656750704030661 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/__init__.py0000664000175000017500000000000013656750631032757 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/test_bgp_speaker_rpc.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/test_bgp_speaker_0000664000175000017500000000331713656750631034267 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib.plugins import directory from neutron.tests import base from neutron_dynamic_routing.api.rpc.handlers import bgp_speaker_rpc from neutron_dynamic_routing.extensions import bgp as bgp_ext class TestBgpSpeakerRpcCallback(base.BaseTestCase): def setUp(self): super(TestBgpSpeakerRpcCallback, self).setUp() self.plugin = mock.Mock() directory.add_plugin(bgp_ext.BGP_EXT_ALIAS, self.plugin) self.callback = bgp_speaker_rpc.BgpSpeakerRpcCallback() def test_get_bgp_speaker_info(self): self.callback.get_bgp_speaker_info(mock.Mock(), bgp_speaker_id='id1') self.assertIsNotNone(len(self.plugin.mock_calls)) def test_get_bgp_peer_info(self): self.callback.get_bgp_peer_info(mock.Mock(), bgp_peer_id='id1') self.assertIsNotNone(len(self.plugin.mock_calls)) def test_get_bgp_speakers(self): self.callback.get_bgp_speakers(mock.Mock(), host='host') self.assertIsNotNone(len(self.plugin.mock_calls)) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/0000775000175000017500000000000013656750704032102 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/__init__.py0000664000175000017500000000000013656750631034200 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/test_bgp_dr_rpc_agent_api.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/test_bgp_dr0000664000175000017500000000744013656750631034325 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import mock from neutron_lib import context from neutron.tests import base from neutron_dynamic_routing.api.rpc.agentnotifiers import bgp_dr_rpc_agent_api class TestBgpDrAgentNotifyApi(base.BaseTestCase): def setUp(self): super(TestBgpDrAgentNotifyApi, self).setUp() self.notifier = ( bgp_dr_rpc_agent_api.BgpDrAgentNotifyApi()) mock_cast_p = mock.patch.object(self.notifier, '_notification_host_cast') self.mock_cast = mock_cast_p.start() mock_call_p = mock.patch.object(self.notifier, '_notification_host_call') self.mock_call = mock_call_p.start() self.context = context.get_admin_context() self.host = 'host-1' def test_agent_updated(self): admin_state_up = True host = 'my-hostname' self.notifier.agent_updated(self.context, admin_state_up, host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_dragent_bgp_routes_advertisement(self): bgp_speaker_id = 'bgp-speaker-1' routes = [{'destination': '1.1.1.1', 'next_hop': '2.2.2.2'}] self.notifier.bgp_routes_advertisement(self.context, bgp_speaker_id, routes, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_dragent_bgp_routes_withdrawal(self): bgp_speaker_id = 'bgp-speaker-1' routes = [{'destination': '1.1.1.1'}] self.notifier.bgp_routes_withdrawal(self.context, bgp_speaker_id, routes, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_peer_disassociated(self): bgp_speaker_id = 'bgp-speaker-1' bgp_peer_ip = '1.1.1.1' self.notifier.bgp_peer_disassociated(self.context, bgp_speaker_id, bgp_peer_ip, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_peer_associated(self): bgp_speaker_id = 'bgp-speaker-1' bgp_peer_id = 'bgp-peer-1' self.notifier.bgp_peer_associated(self.context, bgp_speaker_id, bgp_peer_id, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_speaker_created(self): bgp_speaker_id = 'bgp-speaker-1' self.notifier.bgp_speaker_created(self.context, bgp_speaker_id, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_speaker_removed(self): bgp_speaker_id = 'bgp-speaker-1' self.notifier.bgp_speaker_removed(self.context, bgp_speaker_id, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/db/0000775000175000017500000000000013656750704026111 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/db/__init__.py0000664000175000017500000000000013656750631030207 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/db/test_bgp_dragentscheduler_db.py0000664000175000017500000002122013656750631034336 0ustar zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # # 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_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import importutils from neutron.extensions import agent from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_base_plugin from neutron.tests.unit.extensions import test_agent from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.extensions import bgp from neutron_dynamic_routing.extensions import bgp_dragentscheduler as bgp_dras_ext # noqa from neutron_dynamic_routing.tests.common import helpers from neutron_dynamic_routing.tests.unit.db import test_bgp_db from webob import exc class BgpDrSchedulerTestExtensionManager(object): def get_resources(self): return (agent.Agent.get_resources() + bgp_dras_ext.Bgp_dragentscheduler.get_resources()) def get_actions(self): return [] def get_request_extensions(self): return [] class TestBgpDrSchedulerPlugin(bgp_db.BgpDbMixin, bgp_dras_db.BgpDrAgentSchedulerDbMixin): bgp_drscheduler = importutils.import_object( cfg.CONF.bgp_drscheduler_driver) supported_extension_aliases = ["bgp_dragent_scheduler"] def get_plugin_description(self): return ("BGP dynamic routing service Plugin test class that test " "BGP speaker functionality, with scheduler.") class BgpDrSchedulingTestCase(test_agent.AgentDBTestMixIn, test_bgp_db.BgpEntityCreationMixin): def test_schedule_bgp_speaker(self): """Test happy path over full scheduling cycle.""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] agent_id = agent['id'] data = {'bgp_speaker_id': bgp_speaker_id} req = self.new_create_request('agents', data, self.fmt, agent_id, 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) req_show = self.new_show_request('agents', agent_id, self.fmt, 'bgp-drinstances') res = req_show.get_response(self.ext_api) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(self.fmt, res) self.assertIn('bgp_speakers', res) self.assertTrue(bgp_speaker_id, res['bgp_speakers'][0]['id']) req = self.new_delete_request('agents', agent_id, self.fmt, 'bgp-drinstances', bgp_speaker_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPNoContent.code, res.status_int) res = req_show.get_response(self.ext_api) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(self.fmt, res) self.assertIn('bgp_speakers', res) self.assertEqual([], res['bgp_speakers']) def test_schedule_bgp_speaker_on_invalid_agent(self): """Test error while scheduling BGP speaker on an invalid agent.""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] self._register_l3_agent(host='host1') # Register wrong agent agent = self._list('agents')['agents'][0] data = {'bgp_speaker_id': bgp_speaker_id} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) # Raises an AgentNotFound exception if the agent is invalid self.assertEqual(exc.HTTPNotFound.code, res.status_int) def test_schedule_bgp_speaker_twice_on_same_agent(self): """Test error if a BGP speaker is scheduled twice on same agent""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] data = {'bgp_speaker_id': bgp_speaker_id} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) # Try second time, should raise conflict res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_schedule_bgp_speaker_on_two_different_agents(self): """Test that a BGP speaker can be associated to two agents.""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] helpers.register_bgp_dragent(host='host1') helpers.register_bgp_dragent(host='host2') data = {'bgp_speaker_id': bgp_speaker_id} agent1 = self._list('agents')['agents'][0] req = self.new_create_request( 'agents', data, self.fmt, agent1['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) agent2 = self._list('agents')['agents'][1] req = self.new_create_request( 'agents', data, self.fmt, agent2['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) def test_schedule_multi_bgp_speaker_on_one_dragent(self): """Test only one BGP speaker can be associated to one dragent.""" with self.bgp_speaker(4, 1) as ri1, self.bgp_speaker(4, 2) as ri2: helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] data = {'bgp_speaker_id': ri1['id']} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) data = {'bgp_speaker_id': ri2['id']} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_non_scheduled_bgp_speaker_binding_removal(self): """Test exception while removing an invalid binding.""" with self.bgp_speaker(4, 1234) as ri1: helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] agent_id = agent['id'] self.assertRaises(bgp_dras_ext.DrAgentNotHostingBgpSpeaker, self.bgp_plugin.remove_bgp_speaker_from_dragent, self.context, agent_id, ri1['id']) class BgpDrPluginSchedulerTests(test_db_base_plugin.NeutronDbPluginV2TestCase, BgpDrSchedulingTestCase): def setUp(self, plugin=None, ext_mgr=None, service_plugins=None): if not plugin: plugin = ('neutron_dynamic_routing.tests.unit.db.' 'test_bgp_dragentscheduler_db.TestBgpDrSchedulerPlugin') if not service_plugins: service_plugins = {bgp.BGP_EXT_ALIAS: 'neutron_dynamic_routing.services.bgp.' 'bgp_plugin.BgpPlugin'} ext_mgr = ext_mgr or BgpDrSchedulerTestExtensionManager() super(BgpDrPluginSchedulerTests, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.bgp_plugin = directory.get_plugin(bgp.BGP_EXT_ALIAS) self.context = context.get_admin_context() neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/unit/db/test_bgp_db.py0000664000175000017500000023760413656750631030752 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # 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 contextlib import mock import netaddr from neutron.db import l3_dvr_ha_scheduler_db from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit.plugins.ml2 import test_plugin from neutron_lib.api.definitions import external_net from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_const from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils from neutron_dynamic_routing.extensions import bgp from neutron_dynamic_routing.services.bgp import bgp_plugin _uuid = uuidutils.generate_uuid ADVERTISE_FIPS_KEY = 'advertise_floating_ip_host_routes' IMAGINARY = '2b2334c8-adfe-42d9-82c6-ad866c7fc5d8' # non existent resource id class TestL3Plugin(test_l3.TestL3NatAgentSchedulingServicePlugin, l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin): pass class BgpEntityCreationMixin(object): @contextlib.contextmanager def bgp_speaker(self, ip_version, local_as, name='my-speaker', advertise_fip_host_routes=True, advertise_tenant_networks=True, networks=None, peers=None): data = {'ip_version': ip_version, ADVERTISE_FIPS_KEY: advertise_fip_host_routes, 'advertise_tenant_networks': advertise_tenant_networks, 'local_as': local_as, 'name': name} bgp_speaker = self.bgp_plugin.create_bgp_speaker(self.context, {'bgp_speaker': data}) bgp_speaker_id = bgp_speaker['id'] if networks: for network_id in networks: self.bgp_plugin.add_gateway_network( self.context, bgp_speaker_id, {'network_id': network_id}) if peers: for peer_id in peers: self.bgp_plugin.add_bgp_peer(self.context, bgp_speaker_id, {'bgp_peer_id': peer_id}) yield self.bgp_plugin.get_bgp_speaker(self.context, bgp_speaker_id) @contextlib.contextmanager def bgp_peer(self, tenant_id=_uuid(), remote_as='4321', peer_ip="192.168.1.1", auth_type="md5", password="my-secret", name="my-peer"): data = {'peer_ip': peer_ip, 'tenant_id': tenant_id, 'remote_as': remote_as, 'auth_type': auth_type, 'password': password, 'name': name} bgp_peer = self.bgp_plugin.create_bgp_peer(self.context, {'bgp_peer': data}) yield bgp_peer self.bgp_plugin.delete_bgp_peer(self.context, bgp_peer['id']) @contextlib.contextmanager def bgp_speaker_with_gateway_network(self, address_scope_id, local_as, advertise_fip_host_routes=True, advertise_tenant_networks=True, network_external=True, fmt=None, set_context=False): pass @contextlib.contextmanager def bgp_speaker_with_router(self, address_scope_id, local_as, gw_network_id=None, gw_subnet_ids=None, tenant_subnet_ids=None, advertise_fip_host_routes=True, advertise_tenant_networks=True, fmt=None, set_context=False, router_distributed=False): pass @contextlib.contextmanager def gw_network(self, name='ext-net', external=False, **kwargs): with self.network(name=name, **kwargs) as gw_network: if external: self._update('networks', gw_network['network']['id'], {'network': {external_net.EXTERNAL: True}}) yield gw_network @contextlib.contextmanager def router(self, name='bgp-test-router', tenant_id=_uuid(), admin_state_up=True, **kwargs): request = {'router': {'tenant_id': tenant_id, 'name': name, 'admin_state_up': admin_state_up}} for arg in kwargs: request['router'][arg] = kwargs[arg] router = self.l3plugin.create_router(self.context, request) yield router @contextlib.contextmanager def router_with_external_and_tenant_networks( self, tenant_id=_uuid(), gw_prefix='8.8.8.0/24', tenant_prefix='192.168.0.0/16', address_scope=None, distributed=False, ha=False, ext_net_use_addr_scope=True, tenant_net_use_addr_scope=True): gw_ip_net = netaddr.IPNetwork(gw_prefix) tenant_ip_net = netaddr.IPNetwork(tenant_prefix) ext_pool_args = {'tenant_id': tenant_id, 'name': 'bgp-pool'} tenant_pool_args = ext_pool_args.copy() if address_scope and ext_net_use_addr_scope: ext_pool_args['address_scope_id'] = address_scope['id'] if address_scope and tenant_net_use_addr_scope: tenant_pool_args['address_scope_id'] = address_scope['id'] with self.gw_network(external=True) as ext_net,\ self.network() as int_net,\ self.subnetpool([gw_prefix], **ext_pool_args) as ext_pool,\ self.subnetpool([tenant_prefix], **tenant_pool_args) as int_pool: ext_subnetpool_id = ext_pool['subnetpool']['id'] int_subnetpool_id = int_pool['subnetpool']['id'] gw_net_id = ext_net['network']['id'] with self.subnet(ext_net, cidr=gw_prefix, subnetpool_id=ext_subnetpool_id, ip_version=gw_ip_net.version),\ self.subnet(int_net, cidr=tenant_prefix, subnetpool_id=int_subnetpool_id, ip_version=tenant_ip_net.version) as int_subnet: ext_gw_info = {'network_id': gw_net_id} with self.router(external_gateway_info=ext_gw_info, distributed=distributed, ha=ha) as router: router_id = router['id'] router_interface_info = {'subnet_id': int_subnet['subnet']['id']} self.l3plugin.add_router_interface(self.context, router_id, router_interface_info) yield router, ext_net, int_net class BgpTests(BgpEntityCreationMixin): @contextlib.contextmanager def subnetpool_with_address_scope(self, ip_version, prefixes=None, shared=False, admin=True, name='test-pool', is_default_pool=False, tenant_id=None, **kwargs): if not tenant_id: tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': ip_version, 'shared': shared, 'name': name + '-scope'} address_scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) address_scope_id = address_scope['id'] pool_data = {'tenant_id': tenant_id, 'shared': shared, 'name': name, 'address_scope_id': address_scope_id, 'prefixes': prefixes, 'is_default': is_default_pool} for key in kwargs: pool_data[key] = kwargs[key] yield self.plugin.create_subnetpool(self.context, {'subnetpool': pool_data}) @contextlib.contextmanager def floatingip_from_address_scope_assoc(self, prefixes, address_scope_id, ext_prefixlen=24, int_prefixlen=24): pass def test_add_duplicate_bgp_peer_ip(self): peer_ip = '192.168.1.10' with self.bgp_peer(peer_ip=peer_ip) as peer1,\ self.bgp_peer(peer_ip=peer_ip) as peer2,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234, peers=[peer1['id']]) as speaker: self.assertRaises(bgp.DuplicateBgpPeerIpException, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': peer2['id']}) def test_bgpspeaker_create(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: speaker_name = 'test-speaker' expected_values = [('ip_version', sp['ip_version']), ('name', speaker_name)] with self.bgp_speaker(sp['ip_version'], 1234, name=speaker_name) as bgp_speaker: for k, v in expected_values: self.assertEqual(v, bgp_speaker[k]) def test_bgp_speaker_list(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp1,\ self.subnetpool_with_address_scope(4, prefixes=['9.0.0.0/8']) as sp2: with self.bgp_speaker(sp1['ip_version'], 1234, name='speaker1'),\ self.bgp_speaker(sp2['ip_version'], 4321, name='speaker2'): speakers = self.bgp_plugin.get_bgp_speakers(self.context) self.assertEqual(2, len(speakers)) def test_bgp_speaker_update_local_as(self): local_as_1 = 1234 local_as_2 = 4321 with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], local_as_1) as speaker: self.assertEqual(local_as_1, speaker['local_as']) new_speaker = self.bgp_plugin.update_bgp_speaker( self.context, speaker['id'], {'bgp_speaker': {'local_as': local_as_2}}) self.assertEqual(local_as_2, new_speaker['local_as']) def test_bgp_speaker_show_non_existent(self): self.assertRaises(bgp.BgpSpeakerNotFound, self.bgp_plugin.get_bgp_speaker, self.context, _uuid()) def test_create_bgp_peer(self): args = {'tenant_id': _uuid(), 'remote_as': '1111', 'peer_ip': '10.10.10.10', 'auth_type': 'md5'} with self.bgp_peer(tenant_id=args['tenant_id'], remote_as=args['remote_as'], peer_ip=args['peer_ip'], auth_type='md5', password='my-secret') as peer: self.assertIsNone(peer.get('password')) for key in args: self.assertEqual(args[key], peer[key]) def test_update_bgp_peer_auth_type_none(self): args = {'tenant_id': _uuid(), 'remote_as': '1111', 'peer_ip': '10.10.10.10', 'auth_type': 'md5'} with self.bgp_peer(tenant_id=args['tenant_id'], remote_as=args['remote_as'], peer_ip=args['peer_ip'], auth_type='none', name="my-peer") as peer: data = {'bgp_peer': {'password': "my-secret", 'name': "my-peer1"}} self.assertRaises(bgp.BgpPeerNotAuthenticated, self.bgp_plugin.update_bgp_peer, self.context, peer['id'], data) def test_update_bgp_peer_password_none(self): args = {'tenant_id': _uuid(), 'remote_as': 1111, 'peer_ip': '10.10.10.10', 'name': 'my-peer', 'auth_type': 'none'} with self.bgp_peer(tenant_id=args['tenant_id'], remote_as=args['remote_as'], peer_ip=args['peer_ip'], auth_type=args['auth_type'], name=args['name']) as peer: data = {'bgp_peer': {'name': "my-peer1"}} updated_peer = self.bgp_plugin.update_bgp_peer(self.context, peer['id'], data) for key in args: if key == 'name': self.assertEqual('my-peer1', updated_peer[key]) else: self.assertEqual(peer[key], updated_peer[key]) def test_bgp_peer_show_non_existent(self): self.assertRaises(bgp.BgpPeerNotFound, self.bgp_plugin.get_bgp_peer, self.context, 'unreal-bgp-peer-id') def test_associate_bgp_peer(self): with self.bgp_peer() as peer,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.bgp_plugin.add_bgp_peer(self.context, speaker['id'], {'bgp_peer_id': peer['id']}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertIn('peers', new_speaker) self.assertIn(peer['id'], new_speaker['peers']) self.assertEqual(1, len(new_speaker['peers'])) def test_remove_bgp_peer(self): with self.bgp_peer() as peer,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234, peers=[peer['id']]) as speaker: self.bgp_plugin.remove_bgp_peer(self.context, speaker['id'], {'bgp_peer_id': peer['id']}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertIn('peers', new_speaker) self.assertTrue(not new_speaker['peers']) def test_remove_unassociated_bgp_peer(self): with self.bgp_peer() as peer,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpSpeakerPeerNotAssociated, self.bgp_plugin.remove_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': peer['id']}) def test_remove_non_existent_bgp_peer(self): bgp_peer_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpSpeakerPeerNotAssociated, self.bgp_plugin.remove_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': bgp_peer_id}) def test_add_non_existent_bgp_peer(self): bgp_peer_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpPeerNotFound, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': bgp_peer_id}) def test_add_bgp_peer_without_id(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.BadRequest, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {}) def test_add_bgp_peer_with_bad_id(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.BadRequest, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': 'aaa'}) def test_add_bgp_peer_with_none_id(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.BadRequest, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': None}) def test_add_gateway_network(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker,\ self.gw_network() as network: network_id = network['network']['id'] self.bgp_plugin.add_gateway_network(self.context, speaker['id'], {'network_id': network_id}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertEqual(1, len(new_speaker['networks'])) self.assertTrue(network_id in new_speaker['networks']) def test_create_bgp_speaker_with_network(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp,\ self.gw_network(name='test-net', tenant_id=_uuid(), shared=True) as network: network_id = network['network']['id'] with self.bgp_speaker(sp['ip_version'], 1234, networks=[network_id]) as speaker: self.assertEqual(1, len(speaker['networks'])) self.assertTrue(network_id in speaker['networks']) def test_remove_gateway_network(self): with self.gw_network() as network1,\ self.gw_network() as network2,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: network1_id = network1['network']['id'] network2_id = network2['network']['id'] with self.bgp_speaker(sp['ip_version'], 1234, networks=[network1_id, network2_id]) as speaker: self.bgp_plugin.remove_gateway_network( self.context, speaker['id'], {'network_id': network1_id}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertEqual(1, len(new_speaker['networks'])) def test_add_non_existent_gateway_network(self): network_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.NetworkNotFound, self.bgp_plugin.add_gateway_network, self.context, speaker['id'], {'network_id': network_id}) def test_remove_non_existent_gateway_network(self): network_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpSpeakerNetworkNotAssociated, self.bgp_plugin.remove_gateway_network, self.context, speaker['id'], {'network_id': network_id}) def test_add_gateway_network_two_bgp_speakers_same_scope(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker1,\ self.bgp_speaker(sp['ip_version'], 4321) as speaker2,\ self.gw_network() as network: network_id = network['network']['id'] self.bgp_plugin.add_gateway_network(self.context, speaker1['id'], {'network_id': network_id}) self.bgp_plugin.add_gateway_network(self.context, speaker2['id'], {'network_id': network_id}) speaker1 = self.bgp_plugin.get_bgp_speaker(self.context, speaker1['id']) speaker2 = self.bgp_plugin.get_bgp_speaker(self.context, speaker2['id']) for speaker in [speaker1, speaker2]: self.assertEqual(1, len(speaker['networks'])) self.assertEqual(network_id, speaker['networks'][0]) def test_create_bgp_peer_md5_auth_no_password(self): bgp_peer = {'bgp_peer': {'auth_type': 'md5', 'password': None}} self.assertRaises(bgp.InvalidBgpPeerMd5Authentication, self.bgp_plugin.create_bgp_peer, self.context, bgp_peer) def test__get_address_scope_ids_for_bgp_speaker(self): prefixes1 = ['8.0.0.0/8'] prefixes2 = ['9.0.0.0/8'] prefixes3 = ['10.0.0.0/8'] tenant_id = _uuid() with self.bgp_speaker(4, 1234) as speaker,\ self.subnetpool_with_address_scope(4, prefixes=prefixes1, tenant_id=tenant_id) as sp1,\ self.subnetpool_with_address_scope(4, prefixes=prefixes2, tenant_id=tenant_id) as sp2,\ self.subnetpool_with_address_scope(4, prefixes=prefixes3, tenant_id=tenant_id) as sp3,\ self.gw_network() as network1, self.gw_network() as network2,\ self.gw_network() as network3: network1_id = network1['network']['id'] network2_id = network2['network']['id'] network3_id = network3['network']['id'] base_subnet_data = { 'allocation_pools': n_const.ATTR_NOT_SPECIFIED, 'cidr': n_const.ATTR_NOT_SPECIFIED, 'prefixlen': n_const.ATTR_NOT_SPECIFIED, 'ip_version': 4, 'enable_dhcp': True, 'dns_nameservers': n_const.ATTR_NOT_SPECIFIED, 'host_routes': n_const.ATTR_NOT_SPECIFIED} subnet1_data = {'network_id': network1_id, 'subnetpool_id': sp1['id'], 'name': 'subnet1', 'tenant_id': tenant_id} subnet2_data = {'network_id': network2_id, 'subnetpool_id': sp2['id'], 'name': 'subnet2', 'tenant_id': tenant_id} subnet3_data = {'network_id': network3_id, 'subnetpool_id': sp3['id'], 'name': 'subnet2', 'tenant_id': tenant_id} for k in base_subnet_data: subnet1_data[k] = base_subnet_data[k] subnet2_data[k] = base_subnet_data[k] subnet3_data[k] = base_subnet_data[k] self.plugin.create_subnet(self.context, {'subnet': subnet1_data}) self.plugin.create_subnet(self.context, {'subnet': subnet2_data}) self.plugin.create_subnet(self.context, {'subnet': subnet3_data}) self.bgp_plugin.add_gateway_network(self.context, speaker['id'], {'network_id': network1_id}) self.bgp_plugin.add_gateway_network(self.context, speaker['id'], {'network_id': network2_id}) scopes = self.bgp_plugin._get_address_scope_ids_for_bgp_speaker( self.context, speaker['id']) self.assertEqual(2, len(scopes)) self.assertTrue(sp1['address_scope_id'] in scopes) self.assertTrue(sp2['address_scope_id'] in scopes) def test_get_routes_by_bgp_speaker_binding(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding( self.context, bgp_speaker_id, gw_net_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(tenant_prefix, routes[0]['destination']) self.assertEqual(next_hop, routes[0]['next_hop']) def test_get_routes_by_binding_network(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding( self.context, bgp_speaker_id, gw_net_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(tenant_prefix, routes[0]['destination']) self.assertEqual(next_hop, routes[0]['next_hop']) def _advertised_routes_by_bgp_speaker(self, bgp_speaker_ip_version, local_as, tenant_cidr, gateway_cidr, fip_routes=True, router_distributed=False): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': bgp_speaker_ip_version, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gateway_cidr, tenant_prefix=tenant_cidr, address_scope=scope, distributed=router_distributed) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] with self.bgp_speaker( bgp_speaker_ip_version, local_as, networks=[gw_net_id], advertise_fip_host_routes=fip_routes) as speaker: routes = self.bgp_plugin.get_advertised_routes( self.context, speaker['id']) return routes['advertised_routes'] def test__tenant_prefixes_by_router_no_gateway_port(self): with self.network() as net1, self.network() as net2,\ self.subnetpool_with_address_scope(6, tenant_id=_uuid(), prefixes=['2001:db8::/63']) as pool: subnetpool_id = pool['id'] with self.subnet(network=net1, cidr=None, subnetpool_id=subnetpool_id, ip_version=6) as ext_subnet,\ self.subnet(network=net2, cidr=None, subnetpool_id=subnetpool_id, ip_version=6) as int_subnet,\ self.router() as router: router_id = router['id'] int_subnet_id = int_subnet['subnet']['id'] ext_subnet_id = ext_subnet['subnet']['id'] self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': int_subnet_id}) self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': ext_subnet_id}) with self.bgp_speaker(6, 1234) as speaker: bgp_speaker_id = speaker['id'] cidrs = list(self.bgp_plugin._tenant_prefixes_by_router( self.context, router_id, bgp_speaker_id)) self.assertFalse(cidrs) def test_get_ipv6_tenant_subnet_routes_by_bgp_speaker_ipv6(self): tenant_cidr = '2001:db8::/64' binding_cidr = '2001:ab8::/64' routes = self._advertised_routes_by_bgp_speaker(6, 1234, tenant_cidr, binding_cidr) self.assertEqual(1, len(routes)) dest_prefix = routes[0]['destination'] next_hop = routes[0]['next_hop'] self.assertEqual(tenant_cidr, dest_prefix) self.assertTrue(netaddr.IPSet([binding_cidr]).__contains__(next_hop)) def test_get_ipv4_tenant_subnet_routes_by_bgp_speaker_ipv4(self): tenant_cidr = '172.16.10.0/24' binding_cidr = '20.10.1.0/24' routes = self._advertised_routes_by_bgp_speaker(4, 1234, tenant_cidr, binding_cidr) routes = list(routes) self.assertEqual(1, len(routes)) dest_prefix = routes[0]['destination'] next_hop = routes[0]['next_hop'] self.assertEqual(tenant_cidr, dest_prefix) self.assertTrue(netaddr.IPSet([binding_cidr]).__contains__(next_hop)) def test_get_ipv4_tenant_subnet_routes_by_bgp_speaker_dvr_router(self): tenant_cidr = '172.16.10.0/24' binding_cidr = '20.10.1.0/24' routes = self._advertised_routes_by_bgp_speaker( 4, 1234, tenant_cidr, binding_cidr, router_distributed=True) routes = list(routes) self.assertEqual(1, len(routes)) def test_all_routes_by_bgp_speaker_different_tenant_address_scope(self): binding_cidr = '2001:db8::/64' tenant_cidr = '2002:ab8::/64' with self.subnetpool_with_address_scope(6, tenant_id=_uuid(), prefixes=[binding_cidr]) as ext_pool,\ self.subnetpool_with_address_scope(6, tenant_id=_uuid(), prefixes=[tenant_cidr]) as int_pool,\ self.gw_network(external=True) as ext_net,\ self.network() as int_net: gw_net_id = ext_net['network']['id'] ext_pool_id = ext_pool['id'] int_pool_id = int_pool['id'] with self.subnet(cidr=None, subnetpool_id=ext_pool_id, network=ext_net, ip_version=6) as ext_subnet,\ self.subnet(cidr=None, subnetpool_id=int_pool_id, network=int_net, ip_version=6) as int_subnet,\ self.router() as router: router_id = router['id'] int_subnet_id = int_subnet['subnet']['id'] ext_subnet_id = ext_subnet['subnet']['id'] self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': int_subnet_id}) self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': ext_subnet_id}) with self.bgp_speaker(6, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] cidrs = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) self.assertEqual(0, len(list(cidrs))) def test__get_routes_by_router_with_fip(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_routes_by_router(self.context, router['id']) routes = routes[bgp_speaker_id] next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(2, len(routes)) tenant_prefix_found = False fip_prefix_found = False for route in routes: self.assertEqual(next_hop, route['next_hop']) if route['destination'] == tenant_prefix: tenant_prefix_found = True if route['destination'] == fip_prefix: fip_prefix_found = True self.assertTrue(tenant_prefix_found) self.assertTrue(fip_prefix_found) def test_get_routes_by_bgp_speaker_id_with_fip(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(2, len(routes)) tenant_prefix_found = False fip_prefix_found = False for route in routes: self.assertEqual(next_hop, route['next_hop']) if route['destination'] == tenant_prefix: tenant_prefix_found = True if route['destination'] == fip_prefix: fip_prefix_found = True self.assertTrue(tenant_prefix_found) self.assertTrue(fip_prefix_found) def test_get_routes_by_bgp_speaker_id_with_fip_dvr(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' fixed_prefix = fixed_port['fixed_ips'][0]['ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) routes = list(routes) cvr_gw_ip = ext_gw_info['external_fixed_ips'][0]['ip_address'] dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] self.assertEqual(3, len(routes)) tenant_route_verified = False fip_route_verified = False fixed_ip_route_verified = False for route in routes: if route['destination'] == tenant_prefix: self.assertEqual(cvr_gw_ip, route['next_hop']) tenant_route_verified = True if route['destination'] == fip_prefix: self.assertEqual(dvr_gw_ip, route['next_hop']) fip_route_verified = True if route['destination'] == fixed_prefix: self.assertEqual(dvr_gw_ip, route['next_hop']) fixed_ip_route_verified = True self.assertTrue(tenant_route_verified) self.assertTrue(fip_route_verified) self.assertTrue(fixed_ip_route_verified) def test__get_dvr_fip_host_routes_by_binding(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_dvr_fip_host_routes_by_binding( self.context, gw_net_id, bgp_speaker_id) routes = list(routes) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(dvr_gw_ip, routes[0]['next_hop']) self.assertEqual(fip_prefix, routes[0]['destination']) def test__get_dvr_fip_host_routes_by_router(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_dvr_fip_host_routes_by_router( self.context, bgp_speaker_id, router['id']) routes = list(routes) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(dvr_gw_ip, routes[0]['next_hop']) self.assertEqual(fip_prefix, routes[0]['destination']) def test_get_routes_by_bgp_speaker_binding_with_fip(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding( self.context, bgp_speaker_id, gw_net_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(2, len(routes)) tenant_prefix_found = False fip_prefix_found = False for route in routes: self.assertEqual(next_hop, route['next_hop']) if route['destination'] == tenant_prefix: tenant_prefix_found = True if route['destination'] == fip_prefix: fip_prefix_found = True self.assertTrue(tenant_prefix_found) self.assertTrue(fip_prefix_found) def test__bgp_speakers_for_gateway_network_by_ip_version(self): with self.gw_network(external=True) as ext_net,\ self.bgp_speaker(6, 1234) as s1,\ self.bgp_speaker(6, 4321) as s2: gw_net_id = ext_net['network']['id'] self.bgp_plugin.add_gateway_network(self.context, s1['id'], {'network_id': gw_net_id}) self.bgp_plugin.add_gateway_network(self.context, s2['id'], {'network_id': gw_net_id}) speakers = self.bgp_plugin._bgp_speakers_for_gw_network_by_family( self.context, gw_net_id, 6) self.assertEqual(2, len(speakers)) def test__bgp_speakers_for_gateway_network_by_ip_version_no_binding(self): with self.gw_network(external=True) as ext_net,\ self.bgp_speaker(6, 1234),\ self.bgp_speaker(6, 4321): gw_net_id = ext_net['network']['id'] speakers = self.bgp_plugin._bgp_speakers_for_gw_network_by_family( self.context, gw_net_id, 6) self.assertTrue(not speakers) def _create_scenario_test_l3_agents(self, agent_confs): for item in agent_confs: self.plugin.create_or_update_agent( self.context, {'agent_type': 'L3 agent', 'host': item['host'], 'binary': 'neutron-l3-agent', 'topic': 'test', 'configurations': {"agent_mode": item['mode']}}) def _create_scenario_test_ports(self, tenant_id, port_configs): ports = [] for item in port_configs: port_data = { 'port': {'name': 'test1', 'network_id': item['net_id'], 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: item['host']}} port = self.plugin.create_port(self.context, port_data) ports.append(port) return ports def _create_scenario_test_fips(self, ext_net_id, tenant_id, port_ids): fips = [] for port_id in port_ids: fip_data = {'floatingip': {'floating_network_id': ext_net_id, 'tenant_id': tenant_id, 'port_id': port_id}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fips.append(fip) return fips def test_floatingip_update_callback(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() agent_confs = [{"host": "network1", "mode": "legacy"}] self._create_scenario_test_l3_agents(agent_confs) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] ext_gw_info = router['external_gateway_info'] next_hop = ext_gw_info[ 'external_fixed_ips'][0]['ip_address'] port_configs = [{'net_id': int_net['network']['id'], 'host': 'compute1'}] ports = self._create_scenario_test_ports(tenant_id, port_configs) port_ids = [port['id'] for port in ports] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: with mock.patch.object( self.bgp_plugin, 'start_route_advertisements') as start_adv: with mock.patch.object( self.bgp_plugin, 'stop_route_advertisements') as stop_adv: bgp_speaker_id = speaker['id'] # Create and associate floating IP fips = self._create_scenario_test_fips( gw_net_id, tenant_id, port_ids) fip_id = fips[0]['id'] fip_addr = fips[0]['floating_ip_address'] host_route = {'destination': fip_addr + '/32', 'next_hop': next_hop} start_adv.assert_called_once_with( mock.ANY, mock.ANY, bgp_speaker_id, [host_route]) fip_data = {'floatingip': {'port_id': None}} fip = self.l3plugin.update_floatingip( self.context, fip_id, fip_data) self.assertIsNone(fip['port_id']) # Dissociate floating IP, # withdraw route with next_hop None host_route['next_hop'] = None stop_adv.assert_called_once_with( mock.ANY, mock.ANY, bgp_speaker_id, [host_route]) def _test_legacy_router_fips_next_hop(self, router_ha=False): if router_ha: cfg.CONF.set_override('l3_ha', True) cfg.CONF.set_override('max_l3_agents_per_router', 2) gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() agent_confs = [{"host": "compute1", "mode": "dvr"}, {"host": "compute2", "mode": "dvr"}, {"host": "network1", "mode": "dvr_snat"}] if router_ha: agent_confs.append({"host": "network2", "mode": "dvr_snat"}) self._create_scenario_test_l3_agents(agent_confs) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, ha=router_ha) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] ext_gw_info = router['external_gateway_info'] self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'compute1') self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'compute2') port_configs = [{'net_id': int_net['network']['id'], 'host': 'compute1'}, {'net_id': int_net['network']['id'], 'host': 'compute2'}] ports = self._create_scenario_test_ports(tenant_id, port_configs) port_ids = [port['id'] for port in ports] self._create_scenario_test_fips(gw_net_id, tenant_id, port_ids) next_hop = ext_gw_info[ 'external_fixed_ips'][0]['ip_address'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) routes = list(routes) self.assertEqual(2, len(routes)) for route in routes: self.assertEqual(next_hop, route['next_hop']) def test_legacy_router_fips_has_no_next_hop_to_fip_agent_gateway(self): self._test_legacy_router_fips_next_hop() def test_ha_router_fips_has_no_next_hop_to_fip_agent_gateway(self): self._test_legacy_router_fips_next_hop(router_ha=True) def _test__get_fip_next_hop(self, distributed=False): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() agent_confs = [{"host": "compute1", "mode": "dvr"}, {"host": "network1", "mode": "dvr_snat"}] self._create_scenario_test_l3_agents(agent_confs) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, distributed=distributed) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] legacy_gw_ip = ext_gw_info[ 'external_fixed_ips'][0]['ip_address'] gw_net_id = ext_net['network']['id'] # Whatever the router type is, we always create such dvr fip agent # gateway port, in order to make sure that legacy router fip bgp # route next_hop is not the dvr_fip_agent_gw. fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'compute1') port_configs = [{'net_id': int_net['network']['id'], 'host': 'compute1'}] ports = self._create_scenario_test_ports(tenant_id, port_configs) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': ports[0]['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_address = fip['floating_ip_address'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]): next_hop = self.bgp_plugin._get_fip_next_hop( self.context, router['id'], fip_address) if distributed: self.assertEqual(dvr_gw_ip, next_hop) else: self.assertEqual(legacy_gw_ip, next_hop) def test__get_fip_next_hop_legacy(self): self._test__get_fip_next_hop() def test__get_fip_next_hop_dvr(self): self._test__get_fip_next_hop(distributed=True) def _test__get_dvr_fixed_ip_routes_by_bgp_speaker(self, ext_use_scope, tenant_use_scope): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True, ext_net_use_addr_scope=ext_use_scope, tenant_net_use_addr_scope=tenant_use_scope) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fixed_ip_prefix = fixed_port['fixed_ips'][0]['ip_address'] + '/32' self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_dvr_fixed_ip_routes_by_bgp_speaker( # noqa self.context, bgp_speaker_id) routes = list(routes) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] if ext_use_scope and tenant_use_scope: self.assertEqual(1, len(routes)) self.assertEqual(dvr_gw_ip, routes[0]['next_hop']) self.assertEqual(fixed_ip_prefix, routes[0]['destination']) else: self.assertEqual(0, len(routes)) def test__get_dvr_fixed_ip_routes_by_bgp_speaker_same_scope(self): self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(True, True) def test__get_dvr_fixed_ip_routes_by_bgp_speaker_different_scope(self): self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(True, False) self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(False, True) def test__get_dvr_fixed_ip_routes_by_bgp_speaker_no_scope(self): self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(False, False) def test_get_external_networks_for_port_same_address_scope_v4(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, True) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, True, False) def test_get_external_networks_for_port_different_address_scope_v4(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, False) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, False, False) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, False, True) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, False, True, False) def test_get_external_networks_for_port_same_address_scope_v6(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 6, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, True) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, True, False) def test_get_external_networks_for_port_different_address_scope_v6(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 6, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, False) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, False, False) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, False, True) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, False, True, False) def _test_get_external_networks_for_port(self, gw_prefix, tenant_prefix, scope_data, tenant_id, external_use_address_scope, project_use_address_scope, match_address_scopes=True): scope = None if scope_data: scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True, ext_net_use_addr_scope=external_use_address_scope, tenant_net_use_addr_scope=project_use_address_scope) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) ext_nets = self.bgp_plugin.get_external_networks_for_port( self.context, fixed_port, match_address_scopes=match_address_scopes) if not match_address_scopes: self.assertEqual(1, len(ext_nets)) self.assertEqual(gw_net_id, ext_nets[0]) elif external_use_address_scope and project_use_address_scope: self.assertEqual(1, len(ext_nets)) self.assertEqual(gw_net_id, ext_nets[0]) else: self.assertEqual(0, len(ext_nets)) class Ml2BgpTests(test_plugin.Ml2PluginV2TestCase, BgpTests): fmt = 'json' def setup_parent(self): self.l3_plugin = ('neutron_dynamic_routing.tests.unit.db.test_bgp_db.' 'TestL3Plugin') super(Ml2BgpTests, self).setup_parent() def setUp(self): super(Ml2BgpTests, self).setUp() self.l3plugin = directory.get_plugin(plugin_constants.L3) self.bgp_plugin = bgp_plugin.BgpPlugin() self.plugin = directory.get_plugin() neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/contrib/0000775000175000017500000000000013656750704026205 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/contrib/README0000664000175000017500000000024013656750631027060 0ustar zuulzuul00000000000000The files in this directory are intended for use by the infra jobs that run the various functional test suite in the gate for the neutron-dynamic-routing repo. neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/contrib/gate_hook.sh0000664000175000017500000000716613656750631030512 0ustar zuulzuul00000000000000#!/usr/bin/env bash set -xe PROJECT_NAME=neutron-dynamic-routing GATE_DEST=$BASE/new NEUTRON_PATH=$GATE_DEST/neutron DR_PATH=$GATE_DEST/$PROJECT_NAME DEVSTACK_PATH=$GATE_DEST/devstack APPARMOR_PROFILE_PATH=/etc/apparmor.d QUAGGA_CONFIG_PATH=/tmp/ctn_docker VENV=${1:-"dsvm-functional"} # NOTE(kakuma) # Check apparmor to avoid the following error for docker operation. # "oci runtime error: apparmor failed to apply profile: no such file or directory" # This is a temporary solution. This needs to be fixed in a better way. function check_apparmor_for_docker { if [[ -d $APPARMOR_PROFILE_PATH ]] then if [[ ! -f $APPARMOR_PROFILE_PATH/docker ]] then cat << EOF > /tmp/docker #include profile docker-default flags=(attach_disconnected,mediate_deleted) { #include network, capability, file, umount, deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir) # deny write to files not in /proc//** or /proc/sys/** deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w, deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel) deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/ deny @{PROC}/sysrq-trigger rwklx, deny @{PROC}/mem rwklx, deny @{PROC}/kmem rwklx, deny @{PROC}/kcore rwklx, deny mount, deny /sys/[^f]*/** wklx, deny /sys/f[^s]*/** wklx, deny /sys/fs/[^c]*/** wklx, deny /sys/fs/c[^g]*/** wklx, deny /sys/fs/cg[^r]*/** wklx, deny /sys/firmware/efi/efivars/** rwklx, deny /sys/kernel/security/** rwklx, # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container ptrace (trace,read) peer=docker-default, } EOF chmod 0644 /tmp/docker sudo chown root:root /tmp/docker sudo mv /tmp/docker $APPARMOR_PROFILE_PATH/docker sudo service apparmor restart sudo service docker restart fi fi } function configure_docker_test_env { local docker_pkg sudo bash -c 'echo "tempest ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers' sudo apt-get update sudo apt-get install -y docker-engine || sudo apt-get install -y docker.io } function do_devstack_gate { local gate_retval set +e $GATE_DEST/devstack-gate/devstack-vm-gate.sh gate_retval=$? if [[ -d $QUAGGA_CONFIG_PATH ]] then sudo cp -r $QUAGGA_CONFIG_PATH /opt/stack/logs/bgp_dr_docker fi set -e return $gate_retval } if [[ "$VENV" == dsvm-functional* ]] then # The following need to be set before sourcing # configure_for_func_testing. GATE_STACK_USER=stack IS_GATE=True source $DEVSTACK_PATH/functions source $NEUTRON_PATH/devstack/lib/ovs source $NEUTRON_PATH/tools/configure_for_func_testing.sh enable_plugin $PROJECT_NAME https://opendev.org/openstack/$PROJECT_NAME # Make the workspace owned by the stack user sudo chown -R $STACK_USER:$STACK_USER $BASE elif [[ "$VENV" == dsvm-api* ]] then export DEVSTACK_LOCAL_CONFIG+=$'\n'"NETWORK_API_EXTENSIONS=all" $GATE_DEST/devstack-gate/devstack-vm-gate.sh elif [[ "$VENV" == dsvm-scenario* ]] then sudo apt-get update sudo apt-get install -y --reinstall apparmor configure_docker_test_env check_apparmor_for_docker DEVSTACK_LOCAL_CONFIG+=$'\n'"NETWORK_API_EXTENSIONS=all" export DEVSTACK_LOCAL_CONFIG+=$'\n'"BGP_SCHEDULER_DRIVER=neutron_dynamic_routing.services.bgp.scheduler.bgp_dragent_scheduler.ChanceScheduler" do_devstack_gate else echo "Unrecognized environment $VENV". exit 1 fi neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/0000775000175000017500000000000013656750704026707 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/__init__.py0000664000175000017500000000000013656750631031005 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/0000775000175000017500000000000013656750704030532 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/__init__.py0000664000175000017500000000000013656750631032630 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/0000775000175000017500000000000013656750704031302 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/__init__.py0000664000175000017500000000000013656750631033400 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/0000775000175000017500000000000013656750704033260 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/__init__.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/__ini0000664000175000017500000000000013656750631034245 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017500000000000011220 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/test_bgp_dragent_scheduler.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/test_0000664000175000017500000002010513656750631034316 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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_lib import context import testscenarios from neutron.db import agents_db from neutron.tests.unit import testlib_api from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.services.bgp.scheduler import bgp_dragent_scheduler as bgp_dras # noqa from neutron_dynamic_routing.tests.common import helpers # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios class TestAutoSchedule(testlib_api.SqlTestCase, bgp_dras_db.BgpDrAgentSchedulerDbMixin, agents_db.AgentDbMixin): """Test various scenarios for schedule_unscheduled_bgp_speakers. Below is the brief description of the scenario variables -------------------------------------------------------- host_count number of hosts. agent_count number of BGP dynamic routing agents. down_agent_count number of DRAgents which are inactive. bgp_speaker_count Number of bgp_speakers. hosted_bgp_speakers A mapping of agent id to the ids of the bgp_speakers that they should be initially hosting. expected_schedule_return_value Expected return value of 'schedule_unscheduled_bgp_speakers'. expected_hosted_bgp_speakers This stores the expected bgp_speakers that should have been scheduled (or that could have already been scheduled) for each agent after the 'schedule_unscheduled_bgp_speakers' function is called. """ scenarios = [ ('No BgpDrAgent scheduled, if no DRAgent is present', dict(host_count=1, agent_count=0, down_agent_count=0, bgp_speaker_count=1, hosted_bgp_speakers={}, expected_schedule_return_value=False)), ('No BgpDrAgent scheduled, if no BGP speaker are present', dict(host_count=1, agent_count=1, down_agent_count=0, bgp_speaker_count=0, hosted_bgp_speakers={}, expected_schedule_return_value=False, expected_hosted_bgp_speakers={'agent-0': []})), ('No BgpDrAgent scheduled, if BGP speaker already hosted', dict(host_count=1, agent_count=1, down_agent_count=0, bgp_speaker_count=1, hosted_bgp_speakers={'agent-0': ['bgp-speaker-0']}, expected_schedule_return_value=False, expected_hosted_bgp_speakers={'agent-0': ['bgp-speaker-0']})), ('BgpDrAgent scheduled to the speaker, if the speaker is not hosted', dict(host_count=1, agent_count=1, down_agent_count=0, bgp_speaker_count=1, hosted_bgp_speakers={}, expected_schedule_return_value=True, expected_hosted_bgp_speakers={'agent-0': ['bgp-speaker-0']})), ('No BgpDrAgent scheduled, if all the agents are down', dict(host_count=2, agent_count=2, down_agent_count=2, bgp_speaker_count=1, hosted_bgp_speakers={}, expected_schedule_return_value=False, expected_hosted_bgp_speakers={'agent-0': [], 'agent-1': [], })), ] def _strip_host_index(self, name): """Strips the host index. Eg. if name = '2-agent-3', then 'agent-3' is returned. """ return name[name.find('-') + 1:] def _extract_index(self, name): """Extracts the index number and returns. Eg. if name = '2-agent-3', then 3 is returned """ return int(name.split('-')[-1]) def _get_hosted_bgp_speakers_on_dragent(self, agent_id): query = self.ctx.session.query( bgp_dras_db.BgpSpeakerDrAgentBinding.bgp_speaker_id) query = query.filter( bgp_dras_db.BgpSpeakerDrAgentBinding.agent_id == agent_id) return [item[0] for item in query] def _create_and_set_agents_down(self, hosts, agent_count=0, down_agent_count=0, admin_state_up=True): agents = [] if agent_count: for i, host in enumerate(hosts): is_alive = i >= down_agent_count agents.append(helpers.register_bgp_dragent( host, admin_state_up=admin_state_up, alive=is_alive)) return agents def _save_bgp_speakers(self, bgp_speakers): cls = bgp_db.BgpDbMixin() bgp_speaker_body = { 'bgp_speaker': {'name': 'fake_bgp_speaker', 'ip_version': '4', 'local_as': '123', 'advertise_floating_ip_host_routes': False, 'advertise_tenant_networks': False, 'peers': [], 'networks': []}} i = 1 for bgp_speaker_id in bgp_speakers: bgp_speaker_body['bgp_speaker']['local_as'] = i cls._save_bgp_speaker(self.ctx, bgp_speaker_body, uuid=bgp_speaker_id) i = i + 1 def _test_auto_schedule(self, host_index): scheduler = bgp_dras.ChanceScheduler() self.ctx = context.get_admin_context() msg = 'host_index = %s' % host_index # create hosts hosts = ['%s-agent-%s' % (host_index, i) for i in range(self.host_count)] bgp_dragents = self._create_and_set_agents_down(hosts, self.agent_count, self.down_agent_count) # create bgp_speakers self._bgp_speakers = ['%s-bgp-speaker-%s' % (host_index, i) for i in range(self.bgp_speaker_count)] self._save_bgp_speakers(self._bgp_speakers) # pre schedule the bgp_speakers to the agents defined in # self.hosted_bgp_speakers before calling auto_schedule_bgp_speaker for agent, bgp_speakers in self.hosted_bgp_speakers.items(): agent_index = self._extract_index(agent) for bgp_speaker in bgp_speakers: bs_index = self._extract_index(bgp_speaker) scheduler.bind(self.ctx, [bgp_dragents[agent_index]], self._bgp_speakers[bs_index]) retval = scheduler.schedule_unscheduled_bgp_speakers(self.ctx, hosts[host_index]) self.assertEqual(self.expected_schedule_return_value, retval, message=msg) if self.agent_count: agent_id = bgp_dragents[host_index].id hosted_bgp_speakers = self._get_hosted_bgp_speakers_on_dragent( agent_id) hosted_bs_ids = [self._strip_host_index(net) for net in hosted_bgp_speakers] expected_hosted_bgp_speakers = self.expected_hosted_bgp_speakers[ 'agent-%s' % host_index] self.assertItemsEqual(hosted_bs_ids, expected_hosted_bgp_speakers, msg) def test_auto_schedule(self): for i in range(self.host_count): self._test_auto_schedule(i) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/common/0000775000175000017500000000000013656750704026035 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/common/__init__.py0000664000175000017500000000000013656750631030133 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/tests/common/helpers.py0000664000175000017500000000272513656750631030056 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Development Co # # 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_lib import context from neutron.tests.common import helpers from neutron_dynamic_routing.services.bgp.common import constants as bgp_const def _get_bgp_dragent_dict(host): agent = { 'binary': 'neutron-bgp-dragent', 'host': host, 'topic': 'q-bgp_dragent', 'agent_type': bgp_const.AGENT_TYPE_BGP_ROUTING, 'configurations': {'bgp_speakers': 1}} return agent def register_bgp_dragent(host=helpers.HOST, admin_state_up=True, alive=True): agent = helpers._register_agent( _get_bgp_dragent_dict(host)) if not admin_state_up: helpers.set_agent_admin_state(agent['id']) if not alive: helpers.kill_agent(agent['id']) return helpers.FakePlugin()._get_agent_by_type_and_host( context.get_admin_context(), agent['agent_type'], agent['host']) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/extensions/0000775000175000017500000000000013656750704025602 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/extensions/__init__.py0000664000175000017500000000000013656750631027700 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/extensions/bgp_dragentscheduler.py0000664000175000017500000001357713656750631032343 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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 abc from neutron.api import extensions from neutron.api.v2 import resource from neutron import wsgi from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import agent as agent_exc from neutron_lib.plugins import directory from oslo_log import log as logging import six import webob from neutron_dynamic_routing._i18n import _, _LE from neutron_dynamic_routing.extensions import bgp as bgp_ext LOG = logging.getLogger(__name__) BGP_DRAGENT_SCHEDULER_EXT_ALIAS = 'bgp_dragent_scheduler' BGP_DRINSTANCE = 'bgp-drinstance' BGP_DRINSTANCES = BGP_DRINSTANCE + 's' BGP_DRAGENT = 'bgp-dragent' BGP_DRAGENTS = BGP_DRAGENT + 's' class DrAgentInvalid(agent_exc.AgentNotFound): message = _("BgpDrAgent %(id)s is invalid or has been disabled.") class DrAgentNotHostingBgpSpeaker(n_exc.NotFound): message = _("BGP speaker %(bgp_speaker_id)s is not hosted " "by the BgpDrAgent %(agent_id)s.") class DrAgentAssociationError(n_exc.Conflict): message = _("BgpDrAgent %(agent_id)s is already associated " "to a BGP speaker.") class BgpSpeakerRescheduleError(n_exc.Conflict): message = _("Failed rescheduling %(bgp_speaker_id)s: " "%(failure_reason)s.") class BgpDrSchedulerController(wsgi.Controller): """Schedule BgpSpeaker for a BgpDrAgent""" def get_plugin(self): plugin = directory.get_plugin(bgp_ext.BGP_EXT_ALIAS) if not plugin: LOG.error(_LE('No plugin for BGP routing registered')) msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) return plugin def index(self, request, **kwargs): plugin = self.get_plugin() return plugin.list_bgp_speaker_on_dragent( request.context, kwargs['agent_id']) def create(self, request, body, **kwargs): plugin = self.get_plugin() return plugin.add_bgp_speaker_to_dragent( request.context, kwargs['agent_id'], body['bgp_speaker_id']) def delete(self, request, id, **kwargs): plugin = self.get_plugin() return plugin.remove_bgp_speaker_from_dragent( request.context, kwargs['agent_id'], id) class BgpDrAgentController(wsgi.Controller): def get_plugin(self): plugin = directory.get_plugin(bgp_ext.BGP_EXT_ALIAS) if not plugin: LOG.error(_LE('No plugin for BGP routing registered')) msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) return plugin def index(self, request, **kwargs): plugin = directory.get_plugin(bgp_ext.BGP_EXT_ALIAS) return plugin.list_dragent_hosting_bgp_speaker( request.context, kwargs['bgp_speaker_id']) class Bgp_dragentscheduler(api_extensions.ExtensionDescriptor): """Extension class supporting Dynamic Routing scheduler. """ @classmethod def get_name(cls): return "BGP Dynamic Routing Agent Scheduler" @classmethod def get_alias(cls): return BGP_DRAGENT_SCHEDULER_EXT_ALIAS @classmethod def get_description(cls): return "Schedules BgpSpeakers on BgpDrAgent" @classmethod def get_updated(cls): return "2015-07-30T10:00:00-00:00" @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] parent = dict(member_name="agent", collection_name="agents") controller = resource.Resource(BgpDrSchedulerController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension(BGP_DRINSTANCES, controller, parent)) parent = dict(member_name="bgp_speaker", collection_name="bgp-speakers") controller = resource.Resource(BgpDrAgentController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension(BGP_DRAGENTS, controller, parent)) return exts def get_extended_resources(self, version): return {} @six.add_metaclass(abc.ABCMeta) class BgpDrSchedulerPluginBase(object): """REST API to operate BGP dynamic routing agent scheduler. All the methods must be executed in admin context. """ def get_plugin_description(self): return "Neutron BGP dynamic routing scheduler Plugin" def get_plugin_type(self): return bgp_ext.BGP_EXT_ALIAS @abc.abstractmethod def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id): pass @abc.abstractmethod def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): pass @abc.abstractmethod def list_dragent_hosting_bgp_speaker(self, context, speaker_id): pass @abc.abstractmethod def list_bgp_speaker_on_dragent(self, context, agent_id): pass @abc.abstractmethod def get_bgp_speakers_for_agent_host(self, context, host): pass @abc.abstractmethod def get_bgp_speaker_by_speaker_id(self, context, speaker_id): pass @abc.abstractmethod def get_bgp_peer_by_peer_id(self, context, bgp_peer_id): pass neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/extensions/bgp_4byte_asn.py0000664000175000017500000000450113656750631030673 0ustar zuulzuul00000000000000# 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_lib.api import extensions as api_extensions from neutron_dynamic_routing.extensions import bgp as bgp_ext from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts BGP_4BYTE_ASN_EXT_ALIAS = 'bgp_4byte_asn' RESOURCE_ATTRIBUTE_MAP = { 'bgp-speakers': { 'local_as': {'allow_post': True, 'allow_put': False, 'validate': {'type:range': (bgp_consts.MIN_ASNUM, bgp_consts.MAX_4BYTE_ASNUM)}, 'is_visible': True, 'default': None, 'required_by_policy': False, 'enforce_policy': False} }, 'bgp-peers': { 'remote_as': {'allow_post': True, 'allow_put': False, 'validate': {'type:range': (bgp_consts.MIN_ASNUM, bgp_consts.MAX_4BYTE_ASNUM)}, 'is_visible': True, 'default': None, 'required_by_policy': False, 'enforce_policy': False} } } class Bgp_4byte_asn(api_extensions.ExtensionDescriptor): """Extension class supporting bgp 4-byte AS numbers. """ @classmethod def get_name(cls): return "BGP 4-byte AS numbers" @classmethod def get_alias(cls): return BGP_4BYTE_ASN_EXT_ALIAS @classmethod def get_description(cls): return "Support bgp 4-byte AS numbers" @classmethod def get_updated(cls): return "2017-09-07T00:00:00-00:00" @classmethod def get_resources(cls): return [] def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} def get_required_extensions(self): return [bgp_ext.BGP_EXT_ALIAS] neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/extensions/bgp.py0000664000175000017500000002100313656750631026717 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Development Coompany LP # # 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_lib.api import converters as n_conv from neutron_lib.api import extensions from neutron_lib.db import constants as db_const from neutron_lib import exceptions as n_exc from neutron.api.v2 import resource_helper as rh from neutron_dynamic_routing._i18n import _ from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts BGP_EXT_ALIAS = 'bgp' BGP_SPEAKER_RESOURCE_NAME = 'bgp-speaker' BGP_SPEAKER_BODY_KEY_NAME = 'bgp_speaker' BGP_PEER_BODY_KEY_NAME = 'bgp_peer' RESOURCE_ATTRIBUTE_MAP = { BGP_SPEAKER_RESOURCE_NAME + 's': { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'primary_key': True}, 'name': {'allow_post': True, 'allow_put': True, 'validate': {'type:string': db_const.NAME_FIELD_SIZE}, 'is_visible': True, 'default': ''}, 'local_as': {'allow_post': True, 'allow_put': False, 'validate': {'type:range': (bgp_consts.MIN_ASNUM, bgp_consts.MAX_ASNUM)}, 'is_visible': True, 'default': None, 'required_by_policy': False, 'enforce_policy': False}, 'ip_version': {'allow_post': True, 'allow_put': False, 'validate': {'type:values': [4, 6]}, 'is_visible': True, 'default': None, 'required_by_policy': False, 'enforce_policy': False}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': False, 'validate': { 'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True}, 'peers': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid_list': None}, 'is_visible': True, 'default': [], 'required_by_policy': False, 'enforce_policy': True}, 'networks': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid_list': None}, 'is_visible': True, 'default': [], 'required_by_policy': False, 'enforce_policy': True}, 'advertise_floating_ip_host_routes': { 'allow_post': True, 'allow_put': True, 'convert_to': n_conv.convert_to_boolean, 'validate': {'type:boolean': None}, 'is_visible': True, 'default': True, 'required_by_policy': False, 'enforce_policy': True}, 'advertise_tenant_networks': { 'allow_post': True, 'allow_put': True, 'convert_to': n_conv.convert_to_boolean, 'validate': {'type:boolean': None}, 'is_visible': True, 'default': True, 'required_by_policy': False, 'enforce_policy': True}, }, 'bgp-peers': { 'id': {'allow_post': False, 'allow_put': False, 'validate': {'type:uuid': None}, 'is_visible': True, 'primary_key': True}, 'name': {'allow_post': True, 'allow_put': True, 'validate': {'type:string': db_const.NAME_FIELD_SIZE}, 'is_visible': True, 'default': ''}, 'peer_ip': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'validate': {'type:ip_address': None}, 'is_visible': True}, 'remote_as': {'allow_post': True, 'allow_put': False, 'validate': {'type:range': (bgp_consts.MIN_ASNUM, bgp_consts.MAX_ASNUM)}, 'is_visible': True, 'default': None, 'required_by_policy': False, 'enforce_policy': False}, 'auth_type': {'allow_post': True, 'allow_put': False, 'required_by_policy': True, 'validate': {'type:values': bgp_consts.SUPPORTED_AUTH_TYPES}, 'is_visible': True}, 'password': {'allow_post': True, 'allow_put': True, 'required_by_policy': True, 'validate': {'type:string_or_none': None}, 'is_visible': False, 'default': None}, 'tenant_id': {'allow_post': True, 'allow_put': False, 'required_by_policy': False, 'validate': { 'type:string': db_const.PROJECT_ID_FIELD_SIZE}, 'is_visible': True} } } # Dynamic Routing Exceptions class BgpSpeakerNotFound(n_exc.NotFound): message = _("BGP speaker %(id)s could not be found.") class BgpPeerNotFound(n_exc.NotFound): message = _("BGP peer %(id)s could not be found.") class BgpPeerNotAuthenticated(n_exc.NotFound): message = _("BGP peer %(bgp_peer_id)s not authenticated.") class BgpSpeakerPeerNotAssociated(n_exc.NotFound): message = _("BGP peer %(bgp_peer_id)s is not associated with " "BGP speaker %(bgp_speaker_id)s.") class BgpSpeakerNetworkNotAssociated(n_exc.NotFound): message = _("Network %(network_id)s is not associated with " "BGP speaker %(bgp_speaker_id)s.") class BgpSpeakerNetworkBindingError(n_exc.Conflict): message = _("Network %(network_id)s is already bound to BgpSpeaker " "%(bgp_speaker_id)s.") class NetworkNotBound(n_exc.NotFound): message = _("Network %(network_id)s is not bound to a BgpSpeaker.") class DuplicateBgpPeerIpException(n_exc.Conflict): message = _("BGP Speaker %(bgp_speaker_id)s is already configured to " "peer with a BGP Peer at %(peer_ip)s, it cannot peer with " "BGP Peer %(bgp_peer_id)s.") class InvalidBgpPeerMd5Authentication(n_exc.BadRequest): message = _("A password must be supplied when using auth_type md5.") class NetworkNotBoundForIpVersion(NetworkNotBound): message = _("Network %(network_id)s is not bound to a IPv%(ip_version)s " "BgpSpeaker.") class Bgp(extensions.ExtensionDescriptor): @classmethod def get_name(cls): return "Neutron BGP Dynamic Routing Extension" @classmethod def get_alias(cls): return BGP_EXT_ALIAS @classmethod def get_description(cls): return("Discover and advertise routes for Neutron prefixes " "dynamically via BGP") @classmethod def get_updated(cls): return "2016-05-10T15:37:00-00:00" @classmethod def get_resources(cls): plural_mappings = rh.build_plural_mappings( {}, RESOURCE_ATTRIBUTE_MAP) action_map = {BGP_SPEAKER_RESOURCE_NAME: {'add_bgp_peer': 'PUT', 'remove_bgp_peer': 'PUT', 'add_gateway_network': 'PUT', 'remove_gateway_network': 'PUT', 'get_advertised_routes': 'GET'}} exts = rh.build_resource_info(plural_mappings, RESOURCE_ATTRIBUTE_MAP, BGP_EXT_ALIAS, action_map=action_map) return exts def get_extended_resources(self, version): if version == "2.0": return RESOURCE_ATTRIBUTE_MAP else: return {} def update_attributes_map(self, attributes): super(Bgp, self).update_attributes_map( attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/0000775000175000017500000000000013656750704024154 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/__init__.py0000664000175000017500000000000013656750631026252 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/0000775000175000017500000000000013656750704024740 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/__init__.py0000664000175000017500000000000013656750631027036 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/callbacks/0000775000175000017500000000000013656750704026657 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/callbacks/__init__.py0000664000175000017500000000000013656750631030755 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/callbacks/resources.py0000664000175000017500000000113113656750631031236 0ustar zuulzuul00000000000000# 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. BGP_SPEAKER = 'bgp_speaker' neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/handlers/0000775000175000017500000000000013656750704026540 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/handlers/__init__.py0000664000175000017500000000000013656750631030636 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/handlers/bgp_speaker_rpc.py0000664000175000017500000000470513656750631032245 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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_lib.plugins import directory import oslo_messaging from neutron_dynamic_routing.extensions import bgp as bgp_ext class BgpSpeakerRpcCallback(object): """BgpDrAgent RPC callback in plugin implementations. This class implements the server side of an RPC interface. The client side of this interface can be found in neutron_dynamic_routing.services.bgp.agent.bgp_dragent.BgpDrPluginApi. For more information about changing RPC interfaces, see https://docs.openstack.org/neutron/latest/ contributor/internals/rpc_api.html. """ # API version history: # 1.0 BGPDRPluginApi BASE_RPC_API_VERSION target = oslo_messaging.Target(version='1.0') @property def plugin(self): if not hasattr(self, '_plugin'): self._plugin = directory.get_plugin(bgp_ext.BGP_EXT_ALIAS) return self._plugin def get_bgp_speaker_info(self, context, bgp_speaker_id): """Return BGP Speaker details such as peer list and local_as. Invoked by the BgpDrAgent to lookup the details of a BGP Speaker. """ return self.plugin.get_bgp_speaker_with_advertised_routes( context, bgp_speaker_id) def get_bgp_peer_info(self, context, bgp_peer_id): """Return BgpPeer details such as IP, remote_as, and credentials. Invoked by the BgpDrAgent to lookup the details of a BGP peer. """ return self.plugin.get_bgp_peer(context, bgp_peer_id, ['peer_ip', 'remote_as', 'auth_type', 'password']) def get_bgp_speakers(self, context, host=None, **kwargs): """Returns the list of all BgpSpeakers. Typically invoked by the BgpDrAgent as part of its bootstrap process. """ return self.plugin.get_bgp_speakers_for_agent_host(context, host) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/0000775000175000017500000000000013656750704027761 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/__init__.py0000664000175000017500000000000013656750631032057 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/bgp_dr_rpc_agent_api.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/bgp_dr_rpc_agent_api.p0000664000175000017500000001172213656750631034254 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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_lib import rpc as n_rpc import oslo_messaging from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts class BgpDrAgentNotifyApi(object): """API for plugin to notify BGP DrAgent. This class implements the client side of an rpc interface. The server side is neutron_dynamic_routing.services.bgp.agent.bgp_dragent.BgpDrAgent. For more information about rpc interfaces, please see https://docs.openstack.org/neutron/latest/contributor/internals/rpc_api.html. """ def __init__(self, topic=bgp_consts.BGP_DRAGENT): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) self.topic = topic def agent_updated(self, context, admin_state_up, host): """Tell BgpDrAgent that agent was updated. This effectively tells the bgp_dragent to resync. """ self._notification_host_cast(context, 'agent_updated', {'admin_state_up': admin_state_up}, host) def bgp_routes_advertisement(self, context, bgp_speaker_id, routes, host): """Tell BgpDrAgent to begin advertising the given route. Invoked on FIP association, adding router port to a tenant network, and new DVR port-host bindings, and subnet creation(?). """ self._notification_host_cast(context, 'bgp_routes_advertisement_end', {'advertise_routes': {'speaker_id': bgp_speaker_id, 'routes': routes}}, host) def bgp_routes_withdrawal(self, context, bgp_speaker_id, routes, host): """Tell BgpDrAgent to stop advertising the given route. Invoked on FIP disassociation, removal of a router port on a network, and removal of DVR port-host binding, and subnet delete(?). """ self._notification_host_cast(context, 'bgp_routes_withdrawal_end', {'withdraw_routes': {'speaker_id': bgp_speaker_id, 'routes': routes}}, host) def bgp_peer_disassociated(self, context, bgp_speaker_id, bgp_peer_ip, host): """Tell BgpDrAgent about a BGP Peer disassociation. This effectively tells the BgpDrAgent to stop a peering session. """ self._notification_host_cast(context, 'bgp_peer_disassociation_end', {'bgp_peer': {'speaker_id': bgp_speaker_id, 'peer_ip': bgp_peer_ip}}, host) def bgp_peer_associated(self, context, bgp_speaker_id, bgp_peer_id, host): """Tell BgpDrAgent about a new BGP Peer association. This effectively tells the bgp_dragent to open a peering session. """ self._notification_host_cast(context, 'bgp_peer_association_end', {'bgp_peer': {'speaker_id': bgp_speaker_id, 'peer_id': bgp_peer_id}}, host) def bgp_speaker_created(self, context, bgp_speaker_id, host): """Tell BgpDrAgent about the creation of a BGP Speaker. Because a BGP Speaker can be created with BgpPeer binding in place, we need to inform the BgpDrAgent of a new BGP Speaker in case a peering session needs to opened immediately. """ self._notification_host_cast(context, 'bgp_speaker_create_end', {'bgp_speaker': {'id': bgp_speaker_id}}, host) def bgp_speaker_removed(self, context, bgp_speaker_id, host): """Tell BgpDrAgent about the removal of a BGP Speaker. Because a BGP Speaker can be removed with BGP Peer binding in place, we need to inform the BgpDrAgent of the removal of a BGP Speaker in case peering sessions need to be stopped. """ self._notification_host_cast(context, 'bgp_speaker_remove_end', {'bgp_speaker': {'id': bgp_speaker_id}}, host) def _notification_host_cast(self, context, method, payload, host): """Send payload to BgpDrAgent in the cast mode""" cctxt = self.client.prepare(topic=self.topic, server=host) cctxt.cast(context, method, payload=payload) def _notification_host_call(self, context, method, payload, host): """Send payload to BgpDrAgent in the call mode""" cctxt = self.client.prepare(topic=self.topic, server=host) cctxt.call(context, method, payload=payload) neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/0000775000175000017500000000000013656750704023770 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/__init__.py0000664000175000017500000000000013656750631026066 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/0000775000175000017500000000000013656750704025761 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/__init__.py0000664000175000017500000000000013656750631030057 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/models/0000775000175000017500000000000013656750704027244 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/models/head.py0000664000175000017500000000153213656750631030517 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # 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_lib.db import model_base from neutron_dynamic_routing.db import bgp_db # noqa from neutron_dynamic_routing.db import bgp_dragentscheduler_db # noqa def get_metadata(): return model_base.BASEV2.metadata neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/models/__init__.py0000664000175000017500000000000013656750631031342 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/README0000664000175000017500000000020313656750631026633 0ustar zuulzuul00000000000000For details refer to: https://docs.openstack.org/neutron/latest/contributor/alembic_migrations.html#independent-sub-project-tables neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/0000775000175000017500000000000013656750704031611 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/__init__.py0000664000175000017500000000000013656750631033707 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/0000775000175000017500000000000013656750704033461 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000775000175000017500000000000013656750703034356 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/expand/neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000775000175000017500000000000013656750704034357 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000020600000000000011213 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/expand/f399fa0f5f25_initial.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000664000175000017500000000206113656750631034357 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # 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. # """initial Revision ID: f399fa0f5f25 Revises: None Create Date: 2016-05-03 08:30:18.421995 """ from neutron.db import migration from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = 'f399fa0f5f25' down_revision = 'start_neutron_dynamic_routing' branch_labels = (cli.EXPAND_BRANCH,) # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] def upgrade(): pass ././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000775000175000017500000000000013656750704034357 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000023100000000000011211 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/4cf8bc3edb66_rename_tenant_to_project.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000664000175000017500000000636413656750631034371 0ustar zuulzuul00000000000000# 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. # """rename tenant to project Revision ID: 4cf8bc3edb66 Revises: 61cc795e43e8 Create Date: 2016-07-14 17:32:00.852342 """ from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection from neutron.db import migration # revision identifiers, used by Alembic. revision = '4cf8bc3edb66' down_revision = '61cc795e43e8' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] _INSPECTOR = None def get_inspector(): """Reuse inspector""" global _INSPECTOR if _INSPECTOR: return _INSPECTOR bind = op.get_bind() _INSPECTOR = reflection.Inspector.from_engine(bind) return _INSPECTOR def get_tables(): """ Returns hardcoded list of tables which have ``tenant_id`` column. The list is hardcoded to match the state of the schema when this upgrade script is run. """ tables = [ 'bgp_peers', 'bgp_speakers', ] return tables def get_columns(table): """Returns list of columns for given table.""" inspector = get_inspector() return inspector.get_columns(table) def get_data(): """Returns combined list of tuples: [(table, column)]. The list is built from tables with a tenant_id column. """ output = [] tables = get_tables() for table in tables: columns = get_columns(table) for column in columns: if column['name'] == 'tenant_id': output.append((table, column)) return output def alter_column(table, column): old_name = 'tenant_id' new_name = 'project_id' op.alter_column( table_name=table, column_name=old_name, new_column_name=new_name, existing_type=column['type'], existing_nullable=column['nullable'] ) def recreate_index(index, table_name): old_name = index['name'] new_name = old_name.replace('tenant', 'project') op.drop_index(op.f(old_name), table_name) op.create_index(new_name, table_name, ['project_id']) def upgrade(): """Code reused from Change-Id: I87a8ef342ccea004731ba0192b23a8e79bc382dc """ inspector = get_inspector() data = get_data() for table, column in data: alter_column(table, column) indexes = inspector.get_indexes(table) for index in indexes: if 'tenant_id' in index['name']: recreate_index(index, table) def contract_creation_exceptions(): """Special migration for the blueprint to support Keystone V3. We drop all tenant_id columns and create project_id columns instead. """ return { sa.Column: ['.'.join([table, 'project_id']) for table in get_tables()], sa.Index: get_tables() } ././@LongLink0000000000000000000000000000021000000000000011206 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/61cc795e43e8_initial.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000664000175000017500000000172113656750631034361 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # 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. # """initial Revision ID: 61cc795e43e8 Revises: start_neutron_dynamic_routing Create Date: 2016-05-03 08:30:18.421995 """ from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = '61cc795e43e8' down_revision = 'start_neutron_dynamic_routing' branch_labels = (cli.CONTRACT_BRANCH,) def upgrade(): pass ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/CONTRACT_HEADneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/CONT0000664000175000017500000000001513656750631034142 0ustar zuulzuul00000000000000a589fdb5724c ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/EXPAND_HEADneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/EXPA0000664000175000017500000000001513656750631034134 0ustar zuulzuul00000000000000f399fa0f5f25 ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/quee0000775000175000017500000000000013656750703034340 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016100000000000011213 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/contract/neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/quee0000775000175000017500000000000013656750704034341 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000023100000000000011211 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/contract/a589fdb5724c_change_size_of_as_number.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/quee0000664000175000017500000000225613656750631034347 0ustar zuulzuul00000000000000# 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. # """change size of as number Revision ID: a589fdb5724c Revises: 4cf8bc3edb66 Create Date: 2017-08-31 13:50:28.324422 """ from alembic import op import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = 'a589fdb5724c' down_revision = '4cf8bc3edb66' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.QUEENS] def upgrade(): op.alter_column('bgp_speakers', 'local_as', nullable=False, type_=sa.BigInteger()) op.alter_column('bgp_peers', 'remote_as', nullable=False, type_=sa.BigInteger()) ././@LongLink0000000000000000000000000000020100000000000011206 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/start_neutron_dynamic_routing.pyneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/star0000664000175000017500000000161613656750631034360 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # 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. # """start neutron-dynamic-routing chain Revision ID: start_neutron_dynamic_routing Revises: None Create Date: 2016-04-26 18:42:08.262632 """ # revision identifiers, used by Alembic. revision = 'start_neutron_dynamic_routing' down_revision = None def upgrade(): pass neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/env.py0000664000175000017500000000500013656750631032745 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # 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 logging import config as logging_config from alembic import context from neutron_lib.db import model_base from oslo_config import cfg from oslo_db.sqlalchemy import session import sqlalchemy as sa from sqlalchemy import event from neutron_dynamic_routing.db.migration.models import head # noqa MYSQL_ENGINE = None DR_VERSION_TABLE = 'alembic_version_dr' config = context.config neutron_config = config.neutron_config logging_config.fileConfig(config.config_file_name) target_metadata = model_base.BASEV2.metadata def set_mysql_engine(): try: mysql_engine = neutron_config.command.mysql_engine except cfg.NoSuchOptError: mysql_engine = None global MYSQL_ENGINE MYSQL_ENGINE = (mysql_engine or model_base.BASEV2.__table_args__['mysql_engine']) def run_migrations_offline(): set_mysql_engine() kwargs = dict() if neutron_config.database.connection: kwargs['url'] = neutron_config.database.connection else: kwargs['dialect_name'] = neutron_config.database.engine kwargs['version_table'] = DR_VERSION_TABLE context.configure(**kwargs) with context.begin_transaction(): context.run_migrations() @event.listens_for(sa.Table, 'after_parent_attach') def set_storage_engine(target, parent): if MYSQL_ENGINE: target.kwargs['mysql_engine'] = MYSQL_ENGINE def run_migrations_online(): set_mysql_engine() engine = session.create_engine(neutron_config.database.connection) connection = engine.connect() context.configure( connection=connection, target_metadata=target_metadata, version_table=DR_VERSION_TABLE ) try: with context.begin_transaction(): context.run_migrations() finally: connection.close() engine.dispose() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/script.py.makoneutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/script.py.mak0000664000175000017500000000203313656750631034233 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # 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. # """${message} Revision ID: ${up_revision} Revises: ${down_revision} Create Date: ${create_date} """ # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} % if branch_labels: branch_labels = ${repr(branch_labels)} %endif from alembic import op import sqlalchemy as sa ${imports if imports else ""} def upgrade(): ${upgrades if upgrades else "pass"} neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/bgp_db.py0000664000175000017500000017100213656750631025557 0ustar zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # 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 itertools import netaddr from neutron.db import l3_dvr_db from neutron.db.models import address_scope as address_scope_db from neutron.db.models import l3 as l3_db from neutron.db.models import l3_attrs as l3_attrs_db from neutron.db import models_v2 from neutron.objects import ports from neutron.objects import subnet as subnet_obj from neutron.objects import subnetpool as subnetpool_obj from neutron.plugins.ml2 import models as ml2_models from neutron_lib.api import validators from neutron_lib import constants as lib_consts from neutron_lib.db import api as db_api from neutron_lib.db import model_base from neutron_lib.db import model_query from neutron_lib.db import utils as db_utils from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from oslo_db import exception as oslo_db_exc from oslo_utils import uuidutils import sqlalchemy as sa from sqlalchemy import and_ from sqlalchemy import orm from sqlalchemy.orm import aliased from sqlalchemy.orm import exc as sa_exc from neutron_dynamic_routing._i18n import _ from neutron_dynamic_routing.extensions import bgp as bgp_ext DEVICE_OWNER_ROUTER_GW = lib_consts.DEVICE_OWNER_ROUTER_GW DEVICE_OWNER_ROUTER_INTF = lib_consts.DEVICE_OWNER_ROUTER_INTF DEVICE_OWNER_DVR_INTERFACE = lib_consts.DEVICE_OWNER_DVR_INTERFACE class BgpSpeakerPeerBinding(model_base.BASEV2): """Represents a mapping between BGP speaker and BGP peer""" __tablename__ = 'bgp_speaker_peer_bindings' bgp_speaker_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgp_speakers.id', ondelete='CASCADE'), nullable=False, primary_key=True) bgp_peer_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgp_peers.id', ondelete='CASCADE'), nullable=False, primary_key=True) class BgpSpeakerNetworkBinding(model_base.BASEV2): """Represents a mapping between a network and BGP speaker""" __tablename__ = 'bgp_speaker_network_bindings' bgp_speaker_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgp_speakers.id', ondelete='CASCADE'), nullable=False, primary_key=True) network_id = sa.Column(sa.String(length=36), sa.ForeignKey('networks.id', ondelete='CASCADE'), nullable=False, primary_key=True) ip_version = sa.Column(sa.Integer, nullable=False, autoincrement=False, primary_key=True) class BgpSpeaker(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a BGP speaker""" __tablename__ = 'bgp_speakers' name = sa.Column(sa.String(255), nullable=False) local_as = sa.Column(sa.BigInteger(), nullable=False, autoincrement=False) advertise_floating_ip_host_routes = sa.Column(sa.Boolean, nullable=False) advertise_tenant_networks = sa.Column(sa.Boolean, nullable=False) peers = orm.relationship(BgpSpeakerPeerBinding, backref='bgp_speaker_peer_bindings', cascade='all, delete, delete-orphan', lazy='joined') networks = orm.relationship(BgpSpeakerNetworkBinding, backref='bgp_speaker_network_bindings', cascade='all, delete, delete-orphan', lazy='joined') ip_version = sa.Column(sa.Integer, nullable=False, autoincrement=False) class BgpPeer(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a BGP routing peer.""" __tablename__ = 'bgp_peers' name = sa.Column(sa.String(255), nullable=False) peer_ip = sa.Column(sa.String(64), nullable=False) remote_as = sa.Column(sa.BigInteger(), nullable=False, autoincrement=False) auth_type = sa.Column(sa.String(16), nullable=False) password = sa.Column(sa.String(255), nullable=True) class BgpDbMixin(object): def create_bgp_speaker(self, context, bgp_speaker): uuid = uuidutils.generate_uuid() self._save_bgp_speaker(context, bgp_speaker, uuid) return self.get_bgp_speaker(context, uuid) def get_bgp_speakers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): with db_api.CONTEXT_READER.using(context): return model_query.get_collection( context, BgpSpeaker, self._make_bgp_speaker_dict, filters=filters, fields=fields, sorts=sorts, limit=limit, page_reverse=page_reverse) def get_bgp_speaker(self, context, bgp_speaker_id, fields=None): with db_api.CONTEXT_READER.using(context): bgp_speaker = self._get_bgp_speaker(context, bgp_speaker_id) return self._make_bgp_speaker_dict(bgp_speaker, fields) def get_bgp_speaker_with_advertised_routes(self, context, bgp_speaker_id): bgp_speaker_attrs = ['id', 'local_as', 'tenant_id'] bgp_peer_attrs = ['peer_ip', 'remote_as', 'auth_type', 'password'] with db_api.CONTEXT_READER.using(context): bgp_speaker = self.get_bgp_speaker(context, bgp_speaker_id, fields=bgp_speaker_attrs) res = dict((k, bgp_speaker[k]) for k in bgp_speaker_attrs) res['peers'] = self.get_bgp_peers_by_bgp_speaker(context, bgp_speaker['id'], fields=bgp_peer_attrs) res['advertised_routes'] = self.get_routes_by_bgp_speaker_id( context, bgp_speaker_id) return res def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker): bp = bgp_speaker[bgp_ext.BGP_SPEAKER_BODY_KEY_NAME] with db_api.CONTEXT_WRITER.using(context): bgp_speaker_db = self._get_bgp_speaker(context, bgp_speaker_id) bgp_speaker_db.update(bp) bgp_speaker_dict = self._make_bgp_speaker_dict(bgp_speaker_db) return bgp_speaker_dict def _save_bgp_speaker(self, context, bgp_speaker, uuid): ri = bgp_speaker[bgp_ext.BGP_SPEAKER_BODY_KEY_NAME] ri['tenant_id'] = context.tenant_id with db_api.CONTEXT_WRITER.using(context): res_keys = ['local_as', 'tenant_id', 'name', 'ip_version', 'advertise_floating_ip_host_routes', 'advertise_tenant_networks'] res = dict((k, ri[k]) for k in res_keys) res['id'] = uuid bgp_speaker_db = BgpSpeaker(**res) context.session.add(bgp_speaker_db) def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): bgp_peer_id = self._get_id_for(bgp_peer_info, 'bgp_peer_id') self._save_bgp_speaker_peer_binding(context, bgp_speaker_id, bgp_peer_id) return {'bgp_peer_id': bgp_peer_id} def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): bgp_peer_id = self._get_id_for(bgp_peer_info, 'bgp_peer_id') self._remove_bgp_speaker_peer_binding(context, bgp_speaker_id, bgp_peer_id) return {'bgp_peer_id': bgp_peer_id} def add_gateway_network(self, context, bgp_speaker_id, network_info): network_id = self._get_id_for(network_info, 'network_id') with db_api.CONTEXT_WRITER.using(context): try: self._save_bgp_speaker_network_binding(context, bgp_speaker_id, network_id) except oslo_db_exc.DBDuplicateEntry: raise bgp_ext.BgpSpeakerNetworkBindingError( network_id=network_id, bgp_speaker_id=bgp_speaker_id) return {'network_id': network_id} def remove_gateway_network(self, context, bgp_speaker_id, network_info): with db_api.CONTEXT_WRITER.using(context): network_id = self._get_id_for(network_info, 'network_id') self._remove_bgp_speaker_network_binding(context, bgp_speaker_id, network_id) return {'network_id': network_id} def delete_bgp_speaker(self, context, bgp_speaker_id): with db_api.CONTEXT_WRITER.using(context): bgp_speaker_db = self._get_bgp_speaker(context, bgp_speaker_id) context.session.delete(bgp_speaker_db) def create_bgp_peer(self, context, bgp_peer): ri = bgp_peer[bgp_ext.BGP_PEER_BODY_KEY_NAME] auth_type = ri.get('auth_type') password = ri.get('password') if auth_type == 'md5' and not password: raise bgp_ext.InvalidBgpPeerMd5Authentication() with db_api.CONTEXT_WRITER.using(context): res_keys = ['tenant_id', 'name', 'remote_as', 'peer_ip', 'auth_type', 'password'] res = dict((k, ri[k]) for k in res_keys) res['id'] = uuidutils.generate_uuid() bgp_peer_db = BgpPeer(**res) context.session.add(bgp_peer_db) peer = self._make_bgp_peer_dict(bgp_peer_db) peer.pop('password') return peer def get_bgp_peers(self, context, fields=None, filters=None, sorts=None, limit=None, marker=None, page_reverse=False): return model_query.get_collection(context, BgpPeer, self._make_bgp_peer_dict, filters=filters, fields=fields, sorts=sorts, limit=limit, page_reverse=page_reverse) def get_bgp_peers_by_bgp_speaker(self, context, bgp_speaker_id, fields=None): filters = [BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerPeerBinding.bgp_peer_id == BgpPeer.id] with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpPeer) query = query.filter(*filters) return [self._make_bgp_peer_dict(x, fields) for x in query.all()] def get_bgp_peer(self, context, bgp_peer_id, fields=None): bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id) return self._make_bgp_peer_dict(bgp_peer_db, fields=fields) def delete_bgp_peer(self, context, bgp_peer_id): with db_api.CONTEXT_WRITER.using(context): bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id) context.session.delete(bgp_peer_db) def update_bgp_peer(self, context, bgp_peer_id, bgp_peer): bp = bgp_peer[bgp_ext.BGP_PEER_BODY_KEY_NAME] with db_api.CONTEXT_WRITER.using(context): bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id) if ((bp.get('password') is not None) and (bgp_peer_db['auth_type'] == 'none')): raise bgp_ext.BgpPeerNotAuthenticated(bgp_peer_id=bgp_peer_id) bgp_peer_db.update(bp) bgp_peer_dict = self._make_bgp_peer_dict(bgp_peer_db) return bgp_peer_dict def _get_bgp_speaker(self, context, bgp_speaker_id): try: return model_query.get_by_id(context, BgpSpeaker, bgp_speaker_id) except sa_exc.NoResultFound: raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id) def _get_bgp_speaker_ids_by_router(self, context, router_id): with db_api.CONTEXT_READER.using(context): network_binding = aliased(BgpSpeakerNetworkBinding) r_port = aliased(l3_db.RouterPort) query = context.session.query(network_binding.bgp_speaker_id) query = query.filter( r_port.router_id == router_id, r_port.port_type == lib_consts.DEVICE_OWNER_ROUTER_GW, r_port.port_id == models_v2.Port.id, models_v2.Port.network_id == network_binding.network_id) return [binding.bgp_speaker_id for binding in query.all()] def _get_bgp_speaker_ids_by_binding_network(self, context, network_id): with db_api.CONTEXT_READER.using(context): query = context.session.query( BgpSpeakerNetworkBinding.bgp_speaker_id) query = query.filter( BgpSpeakerNetworkBinding.network_id == network_id) return query.all() def get_advertised_routes(self, context, bgp_speaker_id): routes = self.get_routes_by_bgp_speaker_id(context, bgp_speaker_id) return self._make_advertised_routes_dict(routes) def _get_id_for(self, resource, id_name): try: uuid = resource[id_name] msg = validators.validate_uuid(uuid) except KeyError: msg = _("%s must be specified") % id_name if msg: raise n_exc.BadRequest(resource=bgp_ext.BGP_SPEAKER_RESOURCE_NAME, msg=msg) return uuid def _get_bgp_peers_by_bgp_speaker_binding(self, context, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpPeer) query = query.filter( BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerPeerBinding.bgp_peer_id == BgpPeer.id) return query.all() def _save_bgp_speaker_peer_binding(self, context, bgp_speaker_id, bgp_peer_id): with db_api.CONTEXT_WRITER.using(context): try: bgp_speaker = model_query.get_by_id(context, BgpSpeaker, bgp_speaker_id) except sa_exc.NoResultFound: raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id) try: bgp_peer = model_query.get_by_id(context, BgpPeer, bgp_peer_id) except sa_exc.NoResultFound: raise bgp_ext.BgpPeerNotFound(id=bgp_peer_id) peers = self._get_bgp_peers_by_bgp_speaker_binding(context, bgp_speaker_id) self._validate_peer_ips(bgp_speaker_id, peers, bgp_peer) binding = BgpSpeakerPeerBinding(bgp_speaker_id=bgp_speaker.id, bgp_peer_id=bgp_peer.id) context.session.add(binding) def _validate_peer_ips(self, bgp_speaker_id, current_peers, new_peer): for peer in current_peers: if peer.peer_ip == new_peer.peer_ip: raise bgp_ext.DuplicateBgpPeerIpException( bgp_peer_id=new_peer.id, peer_ip=new_peer.peer_ip, bgp_speaker_id=bgp_speaker_id) def _remove_bgp_speaker_peer_binding(self, context, bgp_speaker_id, bgp_peer_id): with db_api.CONTEXT_WRITER.using(context): try: binding = self._get_bgp_speaker_peer_binding(context, bgp_speaker_id, bgp_peer_id) except sa_exc.NoResultFound: raise bgp_ext.BgpSpeakerPeerNotAssociated( bgp_peer_id=bgp_peer_id, bgp_speaker_id=bgp_speaker_id) context.session.delete(binding) def _save_bgp_speaker_network_binding(self, context, bgp_speaker_id, network_id): with db_api.CONTEXT_WRITER.using(context): try: bgp_speaker = model_query.get_by_id(context, BgpSpeaker, bgp_speaker_id) except sa_exc.NoResultFound: raise bgp_ext.BgpSpeakerNotFound(id=bgp_speaker_id) try: network = model_query.get_by_id(context, models_v2.Network, network_id) except sa_exc.NoResultFound: raise n_exc.NetworkNotFound(net_id=network_id) binding = BgpSpeakerNetworkBinding( bgp_speaker_id=bgp_speaker.id, network_id=network.id, ip_version=bgp_speaker.ip_version) context.session.add(binding) def _remove_bgp_speaker_network_binding(self, context, bgp_speaker_id, network_id): with db_api.CONTEXT_WRITER.using(context): try: binding = self._get_bgp_speaker_network_binding( context, bgp_speaker_id, network_id) except sa_exc.NoResultFound: raise bgp_ext.BgpSpeakerNetworkNotAssociated( network_id=network_id, bgp_speaker_id=bgp_speaker_id) context.session.delete(binding) def _make_bgp_speaker_dict(self, bgp_speaker, fields=None): attrs = {'id', 'local_as', 'tenant_id', 'name', 'ip_version', 'advertise_floating_ip_host_routes', 'advertise_tenant_networks'} peer_bindings = bgp_speaker['peers'] network_bindings = bgp_speaker['networks'] res = dict((k, bgp_speaker[k]) for k in attrs) res['peers'] = [x.bgp_peer_id for x in peer_bindings] res['networks'] = [x.network_id for x in network_bindings] return db_utils.resource_fields(res, fields) def _make_advertised_routes_dict(self, routes): return {'advertised_routes': list(routes)} def _get_bgp_peer(self, context, bgp_peer_id): try: return model_query.get_by_id(context, BgpPeer, bgp_peer_id) except sa_exc.NoResultFound: raise bgp_ext.BgpPeerNotFound(id=bgp_peer_id) def _get_bgp_speaker_peer_binding(self, context, bgp_speaker_id, bgp_peer_id): query = model_query.query_with_hooks(context, BgpSpeakerPeerBinding) return query.filter( BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerPeerBinding.bgp_peer_id == bgp_peer_id).one() def _get_bgp_speaker_network_binding(self, context, bgp_speaker_id, network_id): query = model_query.query_with_hooks(context, BgpSpeakerNetworkBinding) return query.filter( BgpSpeakerNetworkBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerNetworkBinding.network_id == network_id).one() def _make_bgp_peer_dict(self, bgp_peer, fields=None): attrs = ['tenant_id', 'id', 'name', 'peer_ip', 'remote_as', 'auth_type', 'password'] res = dict((k, bgp_peer[k]) for k in attrs) return db_utils.resource_fields(res, fields) def _get_address_scope_ids_for_bgp_speaker(self, context, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): binding = aliased(BgpSpeakerNetworkBinding) address_scope = aliased(address_scope_db.AddressScope) query = context.session.query(address_scope) query = query.filter( binding.bgp_speaker_id == bgp_speaker_id, models_v2.Subnet.ip_version == binding.ip_version, models_v2.Subnet.network_id == binding.network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id) return [scope.id for scope in query.all()] def get_routes_by_bgp_speaker_id(self, context, bgp_speaker_id): """Get all routes that should be advertised by a BgpSpeaker.""" with db_api.CONTEXT_READER.using(context): net_routes = self._get_tenant_network_routes_by_bgp_speaker( context, bgp_speaker_id) fip_routes = self._get_central_fip_host_routes_by_bgp_speaker( context, bgp_speaker_id) dvr_fip_routes = self._get_dvr_fip_host_routes_by_bgp_speaker( context, bgp_speaker_id) dvr_fixedip_routes = self._get_dvr_fixed_ip_routes_by_bgp_speaker( context, bgp_speaker_id) return itertools.chain(fip_routes, net_routes, dvr_fip_routes, dvr_fixedip_routes) def get_routes_by_bgp_speaker_binding(self, context, bgp_speaker_id, network_id): """Get all routes for the given bgp_speaker binding.""" with db_api.CONTEXT_READER.using(context): fip_routes = self._get_central_fip_host_routes_by_binding( context, network_id, bgp_speaker_id) net_routes = self._get_tenant_network_routes_by_binding( context, network_id, bgp_speaker_id) dvr_fip_routes = self._get_dvr_fip_host_routes_by_binding( context, network_id, bgp_speaker_id) return itertools.chain(fip_routes, net_routes, dvr_fip_routes) def _get_routes_by_router(self, context, router_id): bgp_speaker_ids = self._get_bgp_speaker_ids_by_router(context, router_id) route_dict = {} for bgp_speaker_id in bgp_speaker_ids: fip_routes = self._get_central_fip_host_routes_by_router( context, router_id, bgp_speaker_id) net_routes = self._get_tenant_network_routes_by_router( context, router_id, bgp_speaker_id) dvr_fip_routes = self._get_dvr_fip_host_routes_by_router( context, router_id, bgp_speaker_id) routes = itertools.chain(fip_routes, net_routes, dvr_fip_routes) route_dict[bgp_speaker_id] = list(routes) return route_dict def _get_central_fip_host_routes_by_router(self, context, router_id, bgp_speaker_id): """Get floating IP host routes with the given router as nexthop.""" with db_api.CONTEXT_READER.using(context): dest_alias = aliased(l3_db.FloatingIP, name='destination') next_hop_alias = aliased(models_v2.IPAllocation, name='next_hop') binding_alias = aliased(BgpSpeakerNetworkBinding, name='binding') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') query = context.session.query(dest_alias.floating_ip_address, next_hop_alias.ip_address) query = query.join( next_hop_alias, next_hop_alias.network_id == dest_alias.floating_network_id) query = query.join(l3_db.Router, dest_alias.router_id == l3_db.Router.id) query = query.filter( l3_db.Router.id == router_id, dest_alias.router_id == l3_db.Router.id, l3_db.Router.id == router_attrs.router_id, router_attrs.distributed == sa.sql.false(), l3_db.Router.gw_port_id == next_hop_alias.port_id, next_hop_alias.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4, binding_alias.network_id == models_v2.Subnet.network_id, binding_alias.bgp_speaker_id == bgp_speaker_id, binding_alias.ip_version == 4, BgpSpeaker.advertise_floating_ip_host_routes == sa.sql.true()) query = query.outerjoin(router_attrs, l3_db.Router.id == router_attrs.router_id) query = query.filter(router_attrs.distributed != sa.sql.true()) return self._host_route_list_from_tuples(query.all()) def _get_dvr_fip_host_routes_by_router(self, context, bgp_speaker_id, router_id): with db_api.CONTEXT_READER.using(context): gw_query = self._get_gateway_query(context, bgp_speaker_id) fip_query = self._get_fip_query(context, bgp_speaker_id) fip_query.filter(l3_db.FloatingIP.router_id == router_id) #Create the join query join_query = self._join_fip_by_host_binding_to_agent_gateway( context, fip_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _get_central_fip_host_routes_by_binding(self, context, network_id, bgp_speaker_id): """Get all floating IP host routes for the given network binding.""" with db_api.CONTEXT_READER.using(context): # Query the DB for floating IP's and the IP address of the # gateway port dest_alias = aliased(l3_db.FloatingIP, name='destination') next_hop_alias = aliased(models_v2.IPAllocation, name='next_hop') binding_alias = aliased(BgpSpeakerNetworkBinding, name='binding') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') query = context.session.query(dest_alias.floating_ip_address, next_hop_alias.ip_address) query = query.join( next_hop_alias, next_hop_alias.network_id == dest_alias.floating_network_id) query = query.join( binding_alias, binding_alias.network_id == dest_alias.floating_network_id) query = query.join(l3_db.Router, dest_alias.router_id == l3_db.Router.id) query = query.filter( dest_alias.floating_network_id == network_id, dest_alias.router_id == l3_db.Router.id, l3_db.Router.gw_port_id == next_hop_alias.port_id, next_hop_alias.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4, binding_alias.network_id == models_v2.Subnet.network_id, binding_alias.bgp_speaker_id == BgpSpeaker.id, BgpSpeaker.id == bgp_speaker_id, BgpSpeaker.advertise_floating_ip_host_routes == sa.sql.true()) query = query.outerjoin(router_attrs, l3_db.Router.id == router_attrs.router_id) query = query.filter(router_attrs.distributed != sa.sql.true()) return self._host_route_list_from_tuples(query.all()) def _get_dvr_fip_host_routes_by_binding(self, context, network_id, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): BgpBinding = BgpSpeakerNetworkBinding gw_query = self._get_gateway_query(context, bgp_speaker_id) gw_query.filter(BgpBinding.network_id == network_id) fip_query = self._get_fip_query(context, bgp_speaker_id) fip_query.filter(BgpBinding.network_id == network_id) #Create the join query join_query = self._join_fip_by_host_binding_to_agent_gateway( context, fip_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _get_central_fip_host_routes_by_bgp_speaker(self, context, bgp_speaker_id): """Get all the floating IP host routes advertised by a BgpSpeaker.""" with db_api.CONTEXT_READER.using(context): dest_alias = aliased(l3_db.FloatingIP, name='destination') next_hop_alias = aliased(models_v2.IPAllocation, name='next_hop') speaker_binding = aliased(BgpSpeakerNetworkBinding, name="speaker_network_mapping") router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') query = context.session.query(dest_alias.floating_ip_address, next_hop_alias.ip_address) query = query.select_from(dest_alias, BgpSpeaker, l3_db.Router, models_v2.Subnet) query = query.join( next_hop_alias, next_hop_alias.network_id == dest_alias.floating_network_id) query = query.join( speaker_binding, speaker_binding.network_id == dest_alias.floating_network_id) query = query.join(l3_db.Router, dest_alias.router_id == l3_db.Router.id) query = query.filter( BgpSpeaker.id == bgp_speaker_id, BgpSpeaker.advertise_floating_ip_host_routes, speaker_binding.bgp_speaker_id == BgpSpeaker.id, dest_alias.floating_network_id == speaker_binding.network_id, next_hop_alias.network_id == speaker_binding.network_id, dest_alias.router_id == l3_db.Router.id, l3_db.Router.gw_port_id == next_hop_alias.port_id, next_hop_alias.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4) query = query.outerjoin(router_attrs, l3_db.Router.id == router_attrs.router_id) query = query.filter(router_attrs.distributed != sa.sql.true()) return self._host_route_list_from_tuples(query.all()) def _get_gateway_query(self, context, bgp_speaker_id): BgpBinding = BgpSpeakerNetworkBinding AddressScope = address_scope_db.AddressScope ML2PortBinding = ml2_models.PortBinding IpAllocation = models_v2.IPAllocation Port = models_v2.Port gw_query = context.session.query( Port.network_id, ML2PortBinding.host, IpAllocation.ip_address, AddressScope.id.label('address_scope_id')) #Subquery for FIP agent gateway ports gw_query = gw_query.filter( ML2PortBinding.port_id == Port.id, IpAllocation.port_id == Port.id, IpAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == AddressScope.id, Port.network_id == models_v2.Subnet.network_id, Port.device_owner == lib_consts.DEVICE_OWNER_AGENT_GW, Port.network_id == BgpBinding.network_id, BgpBinding.bgp_speaker_id == bgp_speaker_id, BgpBinding.ip_version == 4) return gw_query def _get_fip_query(self, context, bgp_speaker_id): BgpBinding = BgpSpeakerNetworkBinding ML2PortBinding = ml2_models.PortBinding #Subquery for floating IP's fip_query = context.session.query( l3_db.FloatingIP.floating_network_id, ML2PortBinding.host, l3_db.FloatingIP.floating_ip_address) fip_query = fip_query.filter( l3_db.FloatingIP.fixed_port_id == ML2PortBinding.port_id, l3_db.FloatingIP.floating_network_id == BgpBinding.network_id, BgpBinding.bgp_speaker_id == bgp_speaker_id) return fip_query def _get_dvr_fixed_ip_query(self, context, bgp_speaker_id): AddressScope = address_scope_db.AddressScope ML2PortBinding = ml2_models.PortBinding Port = models_v2.Port IpAllocation = models_v2.IPAllocation fixed_ip_query = context.session.query( ML2PortBinding.host, IpAllocation.ip_address, IpAllocation.subnet_id, AddressScope.id.label('address_scope_id')) fixed_ip_query = fixed_ip_query.filter( Port.id == ML2PortBinding.port_id, IpAllocation.port_id == Port.id, Port.device_owner.startswith( lib_consts.DEVICE_OWNER_COMPUTE_PREFIX), IpAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, AddressScope.id == models_v2.SubnetPool.address_scope_id) return fixed_ip_query def _get_dvr_fixed_ip_routes_by_bgp_speaker(self, context, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): gw_query = self._get_gateway_query(context, bgp_speaker_id) fixed_query = self._get_dvr_fixed_ip_query(context, bgp_speaker_id) join_query = self._join_fixed_by_host_binding_to_agent_gateway( context, fixed_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _join_fixed_by_host_binding_to_agent_gateway(self, context, fixed_subq, gw_subq): join_query = context.session.query(fixed_subq.c.ip_address, gw_subq.c.ip_address) and_cond = and_( gw_subq.c.host == fixed_subq.c.host, gw_subq.c.address_scope_id == fixed_subq.c.address_scope_id) return join_query.join(gw_subq, and_cond) def _get_dvr_fip_host_routes_by_bgp_speaker(self, context, bgp_speaker_id): router_attrs = l3_attrs_db.RouterExtraAttributes with db_api.CONTEXT_READER.using(context): gw_query = self._get_gateway_query(context, bgp_speaker_id) fip_query = self._get_fip_query(context, bgp_speaker_id) fip_query = fip_query.filter( l3_db.FloatingIP.router_id == router_attrs.router_id, router_attrs.distributed == sa.sql.true()) #Create the join query join_query = self._join_fip_by_host_binding_to_agent_gateway( context, fip_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _join_fip_by_host_binding_to_agent_gateway(self, context, fip_subq, gw_subq): join_query = context.session.query(fip_subq.c.floating_ip_address, gw_subq.c.ip_address) and_cond = and_( gw_subq.c.host == fip_subq.c.host, gw_subq.c.network_id == fip_subq.c.floating_network_id) return join_query.join(gw_subq, and_cond) def _get_tenant_network_routes_by_binding(self, context, network_id, bgp_speaker_id): """Get all tenant network routes for the given network.""" with db_api.CONTEXT_READER.using(context): tenant_networks_query = self._tenant_networks_by_network_query( context, network_id, bgp_speaker_id) nexthops_query = self._nexthop_ip_addresses_by_binding_query( context, network_id, bgp_speaker_id) join_q = self._join_tenant_networks_to_next_hops( context, tenant_networks_query.subquery(), nexthops_query.subquery()) return self._make_advertised_routes_list(join_q.all()) def _get_tenant_network_routes_by_router(self, context, router_id, bgp_speaker_id): """Get all tenant network routes with the given router as nexthop.""" with db_api.CONTEXT_READER.using(context): scopes = self._get_address_scope_ids_for_bgp_speaker( context, bgp_speaker_id) address_scope = aliased(address_scope_db.AddressScope) inside_query = context.session.query( models_v2.Subnet.cidr, models_v2.IPAllocation.ip_address, address_scope.id) outside_query = context.session.query( address_scope.id, models_v2.IPAllocation.ip_address) speaker_binding = aliased(BgpSpeakerNetworkBinding, name="speaker_network_mapping") port_alias = aliased(l3_db.RouterPort, name='routerport') inside_query = inside_query.filter( port_alias.router_id == router_id, models_v2.IPAllocation.port_id == port_alias.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, address_scope.id.in_(scopes), port_alias.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW, speaker_binding.bgp_speaker_id == bgp_speaker_id) outside_query = outside_query.filter( port_alias.router_id == router_id, port_alias.port_type == lib_consts.DEVICE_OWNER_ROUTER_GW, models_v2.IPAllocation.port_id == port_alias.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, address_scope.id.in_(scopes), speaker_binding.bgp_speaker_id == bgp_speaker_id, speaker_binding.network_id == models_v2.Port.network_id, port_alias.port_id == models_v2.Port.id) inside_query = inside_query.subquery() outside_query = outside_query.subquery() join_query = context.session.query(inside_query.c.cidr, outside_query.c.ip_address) and_cond = and_(inside_query.c.id == outside_query.c.id) join_query = join_query.join(outside_query, and_cond) return self._make_advertised_routes_list(join_query.all()) def _get_tenant_network_routes_by_bgp_speaker(self, context, bgp_speaker_id): """Get all tenant network routes to be advertised by a BgpSpeaker.""" with db_api.CONTEXT_READER.using(context): tenant_nets_q = self._tenant_networks_by_bgp_speaker_query( context, bgp_speaker_id) nexthops_q = self._nexthop_ip_addresses_by_bgp_speaker_query( context, bgp_speaker_id) join_q = self._join_tenant_networks_to_next_hops( context, tenant_nets_q.subquery(), nexthops_q.subquery()) return self._make_advertised_routes_list(join_q.all()) def _join_tenant_networks_to_next_hops(self, context, tenant_networks_subquery, nexthops_subquery): """Join subquery for tenant networks to subquery for nexthop IP's""" left_subq = tenant_networks_subquery right_subq = nexthops_subquery join_query = context.session.query(left_subq.c.cidr, right_subq.c.ip_address) and_cond = and_(left_subq.c.router_id == right_subq.c.router_id, left_subq.c.ip_version == right_subq.c.ip_version) join_query = join_query.join(right_subq, and_cond) return join_query def _tenant_networks_by_network_query(self, context, network_id, bgp_speaker_id): """Return subquery for tenant networks by binding network ID""" address_scope = aliased(address_scope_db.AddressScope, name='address_scope') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') tenant_networks_query = context.session.query( l3_db.RouterPort.router_id, models_v2.Subnet.cidr, models_v2.Subnet.ip_version, address_scope.id) tenant_networks_query = tenant_networks_query.filter( l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW, l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_SNAT, l3_db.RouterPort.router_id == router_attrs.router_id, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.network_id != network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, BgpSpeaker.id == bgp_speaker_id, BgpSpeaker.ip_version == address_scope.ip_version, models_v2.Subnet.ip_version == address_scope.ip_version) return tenant_networks_query def _tenant_networks_by_bgp_speaker_query(self, context, bgp_speaker_id): """Return subquery for tenant networks by binding bgp_speaker_id""" router_id = l3_db.RouterPort.router_id.distinct().label('router_id') tenant_nets_subq = context.session.query(router_id, models_v2.Subnet.cidr, models_v2.Subnet.ip_version) scopes = self._get_address_scope_ids_for_bgp_speaker(context, bgp_speaker_id) filters = self._tenant_networks_by_bgp_speaker_filters(scopes) tenant_nets_subq = tenant_nets_subq.filter(*filters) return tenant_nets_subq def _tenant_networks_by_bgp_speaker_filters(self, address_scope_ids): """Return the filters for querying tenant networks by BGP speaker""" router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') return [models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, l3_db.RouterPort.router_id == router_attrs.router_id, l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW, l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_SNAT, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.network_id != BgpSpeakerNetworkBinding.network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id.in_(address_scope_ids), models_v2.Subnet.ip_version == BgpSpeakerNetworkBinding.ip_version, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id, BgpSpeaker.advertise_tenant_networks == sa.sql.true()] def _nexthop_ip_addresses_by_binding_query(self, context, network_id, bgp_speaker_id): """Return the subquery for locating nexthops by binding network""" nexthops_query = context.session.query( l3_db.RouterPort.router_id, models_v2.IPAllocation.ip_address, models_v2.Subnet.ip_version) filters = self._next_hop_ip_addresses_by_binding_filters( network_id, bgp_speaker_id) nexthops_query = nexthops_query.filter(*filters) return nexthops_query def _next_hop_ip_addresses_by_binding_filters(self, network_id, bgp_speaker_id): """Return the filters for querying nexthops by binding network""" address_scope = aliased(address_scope_db.AddressScope, name='address_scope') return [models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, BgpSpeaker.id == bgp_speaker_id, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id, BgpSpeakerNetworkBinding.network_id == network_id, models_v2.Subnet.network_id == BgpSpeakerNetworkBinding.network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, models_v2.Subnet.ip_version == address_scope.ip_version, l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_GW] def _nexthop_ip_addresses_by_bgp_speaker_query(self, context, bgp_speaker_id): """Return the subquery for locating nexthops by BGP speaker""" nexthops_query = context.session.query( l3_db.RouterPort.router_id, models_v2.IPAllocation.ip_address, models_v2.Subnet.ip_version) filters = self._next_hop_ip_addresses_by_bgp_speaker_filters( bgp_speaker_id) nexthops_query = nexthops_query.filter(*filters) return nexthops_query def _next_hop_ip_addresses_by_bgp_speaker_filters(self, bgp_speaker_id): """Return the filters for querying nexthops by BGP speaker""" router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') return [l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_GW, l3_db.RouterPort.router_id == router_attrs.router_id, BgpSpeakerNetworkBinding.network_id == models_v2.Subnet.network_id, BgpSpeakerNetworkBinding.ip_version == models_v2.Subnet.ip_version, BgpSpeakerNetworkBinding.bgp_speaker_id == bgp_speaker_id, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id] def _tenant_prefixes_by_router(self, context, router_id, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): query = context.session.query(models_v2.Subnet.cidr.distinct()) filters = self._tenant_prefixes_by_router_filters(router_id, bgp_speaker_id) query = query.filter(*filters) return [x[0] for x in query.all()] def _tenant_prefixes_by_router_filters(self, router_id, bgp_speaker_id): binding = aliased(BgpSpeakerNetworkBinding, name='network_binding') subnetpool = aliased(models_v2.SubnetPool, name='subnetpool') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') return [models_v2.Subnet.id == models_v2.IPAllocation.subnet_id, models_v2.Subnet.subnetpool_id == subnetpool.id, l3_db.RouterPort.router_id == router_id, l3_db.Router.id == l3_db.RouterPort.router_id, l3_db.Router.id == router_attrs.router_id, l3_db.Router.gw_port_id == models_v2.Port.id, models_v2.Port.network_id == binding.network_id, binding.bgp_speaker_id == BgpSpeaker.id, l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id] def _tenant_prefixes_by_router_interface(self, context, router_port_id, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): query = context.session.query(models_v2.Subnet.cidr.distinct()) filters = self._tenant_prefixes_by_router_filters(router_port_id, bgp_speaker_id) query = query.filter(*filters) return [x[0] for x in query.all()] def _tenant_prefixes_by_router_port_filters(self, router_port_id, bgp_speaker_id): binding = aliased(BgpSpeakerNetworkBinding, name='network_binding') return [models_v2.Subnet.id == models_v2.IPAllocation.subnet_id, l3_db.RouterPort.port_id == router_port_id, l3_db.Router.id == l3_db.RouterPort.router_id, l3_db.Router.gw_port_id == models_v2.Port.id, models_v2.Port.network_id == binding.network_id, binding.bgp_speaker_id == BgpSpeaker.id, models_v2.Subnet.ip_version == binding.ip_version, l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id] def _bgp_speakers_for_gateway_network(self, context, network_id): """Return all BgpSpeakers for the given gateway network""" with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpSpeaker) query = query.filter( BgpSpeakerNetworkBinding.network_id == network_id, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id) return query.all() def _bgp_speakers_for_gw_network_by_family(self, context, network_id, ip_version): """Return the BgpSpeaker by given gateway network and ip_version""" with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpSpeaker) query = query.filter( BgpSpeakerNetworkBinding.network_id == network_id, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id, BgpSpeakerNetworkBinding.ip_version == ip_version) return query.all() def _make_advertised_routes_list(self, routes): route_list = ({'destination': x, 'next_hop': y} for x, y in routes) return route_list def _route_list_from_prefixes_and_next_hop(self, routes, next_hop): route_list = [{'destination': x, 'next_hop': next_hop} for x in routes] return route_list def _host_route_list_from_tuples(self, ip_next_hop_tuples): """Return the list of host routes given a list of (IP, nexthop)""" return ({'destination': x + '/32', 'next_hop': y} for x, y in ip_next_hop_tuples) def _get_router(self, context, router_id): try: router = model_query.get_by_id(context, l3_db.Router, router_id) except sa_exc.NoResultFound: raise l3_exc.RouterNotFound(router_id=router_id) return router def _get_fip_next_hop(self, context, router_id, ip_address=None): router = self._get_router(context, router_id) gw_port = router.gw_port if not gw_port: return if l3_dvr_db.is_distributed_router(router) and ip_address: return self._get_dvr_fip_next_hop(context, ip_address) for fixed_ip in gw_port.fixed_ips: addr = netaddr.IPAddress(fixed_ip.ip_address) if addr.version == 4: return fixed_ip.ip_address def _get_dvr_fip_agent_gateway_query(self, context): ML2PortBinding = ml2_models.PortBinding IpAllocation = models_v2.IPAllocation Port = models_v2.Port base_query = context.session.query(Port.network_id, ML2PortBinding.host, IpAllocation.ip_address) gw_query = base_query.filter( ML2PortBinding.port_id == Port.id, IpAllocation.port_id == Port.id, Port.device_owner == lib_consts.DEVICE_OWNER_AGENT_GW) return gw_query def _get_fip_fixed_port_host_query(self, context, fip_address): ML2PortBinding = ml2_models.PortBinding fip_query = context.session.query( l3_db.FloatingIP.floating_network_id, ML2PortBinding.host, l3_db.FloatingIP.floating_ip_address) fip_query = fip_query.filter( l3_db.FloatingIP.fixed_port_id == ML2PortBinding.port_id, l3_db.FloatingIP.floating_ip_address == fip_address) return fip_query def _get_dvr_fip_next_hop(self, context, fip_address): try: dvr_agent_gw_query = self._get_dvr_fip_agent_gateway_query( context) fip_fix_port_query = self._get_fip_fixed_port_host_query( context, fip_address) q = self._join_fip_by_host_binding_to_agent_gateway( context, fip_fix_port_query.subquery(), dvr_agent_gw_query.subquery()).one() return q[1] except sa_exc.NoResultFound: return except sa_exc.MultipleResultsFound: return def get_external_networks_for_port(self, ctx, port, match_address_scopes=True): with db_api.CONTEXT_READER.using(ctx): # Retrieve address scope info for the supplied port port_fixed_ips = port.get('fixed_ips') if not port_fixed_ips: return [] subnets_filter = {'id': [x['subnet_id'] for x in port_fixed_ips]} port_subnets = subnet_obj.Subnet.get_objects(ctx, **subnets_filter) port_subnetpools = subnetpool_obj.SubnetPool.get_objects( ctx, id=[x.subnetpool_id for x in port_subnets]) port_scopes = set([x.address_scope_id for x in port_subnetpools]) if match_address_scopes and len(port_scopes) == 0: return [] # Get all router IDs with an interface on the given port's network router_iface_filters = {'device_owner': [DEVICE_OWNER_ROUTER_INTF, DEVICE_OWNER_DVR_INTERFACE], 'network_id': port['network_id']} router_ids = [x.device_id for x in ports.Port.get_objects( ctx, **router_iface_filters)] # Retrieve the gateway ports for the identified routers gw_port_filters = {'device_owner': DEVICE_OWNER_ROUTER_GW, 'device_id': router_ids} gw_ports = ports.Port.get_objects(ctx, **gw_port_filters) # If we don't need to match address scopes, return here if not match_address_scopes: return list(set([x.network_id for x in gw_ports])) # Retrieve address scope info for associated gateway networks gw_fixed_ips = [] for gw_port in gw_ports: gw_fixed_ips.extend(gw_port.fixed_ips) gw_subnet_filters = {'id': [x.subnet_id for x in gw_fixed_ips]} gw_subnets = subnet_obj.Subnet.get_objects(ctx, **gw_subnet_filters) ext_net_subnetpool_map = {} for gw_subnet in gw_subnets: ext_net_id = gw_subnet.network_id ext_pool = subnetpool_obj.SubnetPool.get_object( ctx, id=gw_subnet.subnetpool_id) ext_scope_set = ext_net_subnetpool_map.get(ext_net_id, set()) ext_scope_set.add(ext_pool.address_scope_id) ext_net_subnetpool_map[ext_net_id] = ext_scope_set ext_nets = [] # Match address scopes between port and gateway network(s) for net in ext_net_subnetpool_map.keys(): ext_scopes = ext_net_subnetpool_map[net] if ext_scopes.issubset(port_scopes): ext_nets.append(net) return ext_nets neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/db/bgp_dragentscheduler_db.py0000664000175000017500000002733413656750631031172 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # 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_lib import context as ncontext from neutron_lib.db import api as db_api from neutron_lib.db import model_base from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.orm import exc from neutron.db import agentschedulers_db as as_db from neutron.db.models import agent as agent_model from neutron_dynamic_routing._i18n import _ from neutron_dynamic_routing._i18n import _LW from neutron_dynamic_routing.extensions import bgp_dragentscheduler as bgp_dras_ext # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts LOG = logging.getLogger(__name__) BGP_DRAGENT_SCHEDULER_OPTS = [ cfg.StrOpt( 'bgp_drscheduler_driver', default='neutron_dynamic_routing.services.bgp.scheduler' '.bgp_dragent_scheduler.ChanceScheduler', help=_('Driver used for scheduling BGP speakers to BGP DrAgent')) ] cfg.CONF.register_opts(BGP_DRAGENT_SCHEDULER_OPTS) class BgpSpeakerDrAgentBinding(model_base.BASEV2): """Represents a mapping between BGP speaker and BGP DRAgent""" __tablename__ = 'bgp_speaker_dragent_bindings' bgp_speaker_id = sa.Column(sa.String(length=36), sa.ForeignKey("bgp_speakers.id", ondelete='CASCADE'), nullable=False) dragent = orm.relation(agent_model.Agent) agent_id = sa.Column(sa.String(length=36), sa.ForeignKey("agents.id", ondelete='CASCADE'), primary_key=True) class BgpDrAgentSchedulerDbMixin(bgp_dras_ext.BgpDrSchedulerPluginBase, as_db.AgentSchedulerDbMixin): bgp_drscheduler = None def add_periodic_dragent_status_check(self): if self.bgp_drscheduler: self.add_agent_status_check_worker( self.remove_bgp_speaker_from_down_dragents) self.add_agent_status_check_worker( self.schedule_all_unscheduled_bgp_speakers) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def schedule_all_unscheduled_bgp_speakers(self): context = ncontext.get_admin_context() if self.bgp_drscheduler: return self.bgp_drscheduler.schedule_all_unscheduled_bgp_speakers( context) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def schedule_unscheduled_bgp_speakers(self, context, host): if self.bgp_drscheduler: return self.bgp_drscheduler.schedule_unscheduled_bgp_speakers( context, host) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def schedule_bgp_speaker(self, context, created_bgp_speaker): if self.bgp_drscheduler: agents = self.bgp_drscheduler.schedule(self, context, created_bgp_speaker) for agent in agents: self._bgp_rpc.bgp_speaker_created(context, created_bgp_speaker['id'], agent.host) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id): """Associate a BgpDrAgent with a BgpSpeaker.""" try: self._save_bgp_speaker_dragent_binding(context, agent_id, speaker_id) except db_exc.DBDuplicateEntry: raise bgp_dras_ext.DrAgentAssociationError( agent_id=agent_id) LOG.debug('BgpSpeaker %(bgp_speaker_id)s added to ' 'BgpDrAgent %(agent_id)s', {'bgp_speaker_id': speaker_id, 'agent_id': agent_id}) def _save_bgp_speaker_dragent_binding(self, context, agent_id, speaker_id): with db_api.CONTEXT_WRITER.using(context): agent_db = self._get_agent(context, agent_id) agent_up = agent_db['admin_state_up'] is_agent_bgp = (agent_db['agent_type'] == bgp_consts.AGENT_TYPE_BGP_ROUTING) if not is_agent_bgp or not agent_up: raise bgp_dras_ext.DrAgentInvalid(id=agent_id) binding = BgpSpeakerDrAgentBinding() binding.bgp_speaker_id = speaker_id binding.agent_id = agent_id context.session.add(binding) self._bgp_rpc.bgp_speaker_created(context, speaker_id, agent_db.host) def remove_bgp_speaker_from_down_dragents(self): self.reschedule_resources_from_down_agents( agent_type=bgp_consts.AGENT_TYPE_BGP_ROUTING, get_down_bindings=self.get_down_bgp_speaker_bindings, agent_id_attr='agent_id', resource_id_attr='bgp_speaker_id', resource_name='bgp_speaker', reschedule_resource=self.reschedule_bgp_speaker, rescheduling_failed=bgp_dras_ext.BgpSpeakerRescheduleError) def get_down_bgp_speaker_bindings(self, context, agent_dead_limit): cutoff = self.get_cutoff_time(agent_dead_limit) query = ( context.session.query(BgpSpeakerDrAgentBinding). join(agent_model.Agent). filter(agent_model.Agent.heartbeat_timestamp < cutoff, agent_model.Agent.admin_state_up)) down_bindings = [b for b in query] return down_bindings def reschedule_bgp_speaker(self, context, bgp_speaker_id): dragent = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id])[0] bgp_speaker = self.get_bgp_speaker(context, bgp_speaker_id) dragent_id = dragent.id with db_api.CONTEXT_WRITER.using(context): self._remove_bgp_speaker_from_dragent( context, dragent_id, bgp_speaker_id) self.schedule_bgp_speaker(context, bgp_speaker) new_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) if new_dragents == [] or new_dragents[0].id == dragent.id: raise bgp_dras_ext.BgpSpeakerRescheduleError( bgp_speaker_id=bgp_speaker_id, failure_reason="no eligible dr agent found") def _remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): with db_api.CONTEXT_WRITER.using(context): agent_db = self._get_agent(context, agent_id) is_agent_bgp = (agent_db['agent_type'] == bgp_consts.AGENT_TYPE_BGP_ROUTING) if not is_agent_bgp: raise bgp_dras_ext.DrAgentInvalid(id=agent_id) query = context.session.query(BgpSpeakerDrAgentBinding) query = query.filter_by(bgp_speaker_id=speaker_id, agent_id=agent_id) num_deleted = query.delete() if not num_deleted: raise bgp_dras_ext.DrAgentNotHostingBgpSpeaker( bgp_speaker_id=speaker_id, agent_id=agent_id) LOG.debug('BgpSpeaker %(bgp_speaker_id)s removed from ' 'BgpDrAgent %(agent_id)s', {'bgp_speaker_id': speaker_id, 'agent_id': agent_id}) def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): self._remove_bgp_speaker_from_dragent(context, agent_id, speaker_id) agent_db = self._get_agent(context, agent_id) self._bgp_rpc.bgp_speaker_removed(context, speaker_id, agent_db.host) def get_dragents_hosting_bgp_speakers(self, context, bgp_speaker_ids, active=None, admin_state_up=None): query = context.session.query(BgpSpeakerDrAgentBinding) query = query.options(orm.contains_eager( BgpSpeakerDrAgentBinding.dragent)) query = query.join(BgpSpeakerDrAgentBinding.dragent) if len(bgp_speaker_ids) == 1: query = query.filter( BgpSpeakerDrAgentBinding.bgp_speaker_id == ( bgp_speaker_ids[0])) elif bgp_speaker_ids: query = query.filter( BgpSpeakerDrAgentBinding.bgp_speaker_id in bgp_speaker_ids) if admin_state_up is not None: query = query.filter(agent_model.Agent.admin_state_up == admin_state_up) return [binding.dragent for binding in query if as_db.AgentSchedulerDbMixin.is_eligible_agent( active, binding.dragent)] def get_dragent_bgp_speaker_bindings(self, context): return context.session.query(BgpSpeakerDrAgentBinding).all() def list_dragent_hosting_bgp_speaker(self, context, speaker_id): dragents = self.get_dragents_hosting_bgp_speakers(context, [speaker_id]) agent_ids = [dragent.id for dragent in dragents] if not agent_ids: return {'agents': []} return {'agents': self.get_agents(context, filters={'id': agent_ids})} def list_bgp_speaker_on_dragent(self, context, agent_id): query = context.session.query(BgpSpeakerDrAgentBinding.bgp_speaker_id) query = query.filter_by(agent_id=agent_id) bgp_speaker_ids = [item[0] for item in query] if not bgp_speaker_ids: # Exception will be thrown if the requested agent does not exist. self._get_agent(context, agent_id) return {'bgp_speakers': []} return {'bgp_speakers': self.get_bgp_speakers(context, filters={'id': bgp_speaker_ids})} def get_bgp_speakers_for_agent_host(self, context, host): agent = self._get_agent_by_type_and_host( context, bgp_consts.AGENT_TYPE_BGP_ROUTING, host) if not agent.admin_state_up: return {} query = context.session.query(BgpSpeakerDrAgentBinding) query = query.filter(BgpSpeakerDrAgentBinding.agent_id == agent.id) try: binding = query.one() except exc.NoResultFound: return [] bgp_speaker = self.get_bgp_speaker_with_advertised_routes( context, binding['bgp_speaker_id']) return [bgp_speaker] def get_bgp_speaker_by_speaker_id(self, context, bgp_speaker_id): try: return self.get_bgp_speaker(context, bgp_speaker_id) except exc.NoResultFound: return {} def get_bgp_peer_by_peer_id(self, context, bgp_peer_id): try: return self.get_bgp_peer(context, bgp_peer_id) except exc.NoResultFound: return {} neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/0000775000175000017500000000000013656750704024146 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/__init__.py0000664000175000017500000000000013656750631026244 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/eventlet/0000775000175000017500000000000013656750704025774 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/eventlet/__init__.py0000664000175000017500000000170413656750631030106 0ustar zuulzuul00000000000000# 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 eventlet eventlet.monkey_patch() # Monkey patch the original current_thread to use the up-to-date _active # global variable. See https://bugs.launchpad.net/bugs/1863021 and # https://github.com/eventlet/eventlet/issues/592 import __original_module_threading as orig_threading # noqa import threading # noqa orig_threading.current_thread.__globals__['_active'] = threading._active neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/eventlet/agents/0000775000175000017500000000000013656750704027255 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/eventlet/agents/__init__.py0000664000175000017500000000000013656750631031353 0ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/cmd/eventlet/agents/bgp_dragent.py0000664000175000017500000000131213656750631032077 0ustar zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # 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_dynamic_routing.services.bgp.agent import entry as bgp_dragent def main(): bgp_dragent.main() neutron-dynamic-routing-16.0.0/neutron_dynamic_routing/_i18n.py0000664000175000017500000000253313656750631024675 0ustar zuulzuul00000000000000# 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 oslo_i18n DOMAIN = "neutron_dynamic_routing" _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" _C = _translators.contextual_form # The plural translation function using the name "_P" _P = _translators.plural_form # Translators for log levels. # # The abbreviated names are meant to reflect the usual use of a short # name like '_'. The "L" is for "log" and the other letter comes from # the level. _LI = _translators.log_info _LW = _translators.log_warning _LE = _translators.log_error _LC = _translators.log_critical def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) neutron-dynamic-routing-16.0.0/PKG-INFO0000664000175000017500000000374013656750704017537 0ustar zuulzuul00000000000000Metadata-Version: 1.2 Name: neutron-dynamic-routing Version: 16.0.0 Summary: Neutron Dynamic Routing Home-page: https://docs.openstack.org/neutron-dynamic-routing/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/neutron-dynamic-routing.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on This package contains neutron-dynamic-routing code which depends upon neutron and it's related libraries to run. Project Resources ================= The homepage for Neutron is: https://launchpad.net/neutron. Use this site for asking for help, and filing bugs. We use a single launchpad page for all Neutron projects. Code is available on opendev.org at: https://opendev.org/openstack/neutron-dynamic-routing Refer to Neutron documentation for more information: `Neutron README.rst `_ Release notes for the project can be found at: https://docs.openstack.org/releasenotes/neutron-dynamic-routing/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 neutron-dynamic-routing-16.0.0/test-requirements.txt0000664000175000017500000000146213656750631022701 0ustar zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking>=1.1.0,<1.2.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 mock>=2.0.0 # BSD python-subunit>=1.0.0 # Apache-2.0/BSD sphinx!=1.6.6,!=1.6.7,!=2.1.0,>=1.6.5 # BSD sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 oslo.concurrency>=3.26.0 # Apache-2.0 stestr>=1.0.0 # Apache-2.0 testresources>=2.0.0 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD WebOb>=1.7.1 # MIT WebTest>=2.0.27 # MIT oslotest>=3.2.0 # Apache-2.0 reno>=2.5.0 # Apache-2.0 tempest>=17.1.0 # Apache-2.0 neutron-dynamic-routing-16.0.0/lower-constraints.txt0000664000175000017500000000514113656750631022674 0ustar zuulzuul00000000000000alabaster==0.7.10 alembic==0.8.10 amqp==2.1.1 appdirs==1.3.0 asn1crypto==0.23.0 Babel==2.3.4 beautifulsoup4==4.6.0 cachetools==2.0.0 cffi==1.7.0 cliff==2.8.0 cmd2==0.8.0 contextlib2==0.4.0 coverage==4.0 cryptography==2.1 debtcollector==1.2.0 decorator==3.4.0 deprecation==1.0 docutils==0.11 dogpile.cache==0.6.2 dulwich==0.15.0 eventlet==0.18.2 extras==1.0.0 fasteners==0.7.0 fixtures==3.0.0 flake8==2.5.5 flake8-import-order==0.12 future==0.16.0 futurist==1.2.0 greenlet==0.4.10 hacking==0.11.0 httplib2==0.9.1 idna==2.6 imagesize==0.7.1 iso8601==0.1.11 Jinja2==2.10 jmespath==0.9.0 jsonpatch==1.16 jsonpointer==1.13 jsonschema==2.6.0 keystoneauth1==3.4.0 keystonemiddleware==4.17.0 kombu==4.0.0 linecache2==1.0.0 logutils==0.3.5 Mako==0.4.0 MarkupSafe==1.0 mccabe==0.2.1 mock==2.0.0 monotonic==0.6 mox3==0.20.0 msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 neutron==14.0.0.0b1 neutron-lib==1.26.0 openstackdocstheme==1.18.1 openstacksdk==0.11.2 os-client-config==1.28.0 os-ken==0.3.0 os-service-types==1.2.0 os-xenapi==0.3.1 osc-lib==1.8.0 oslo.cache==1.26.0 oslo.concurrency==3.26.0 oslo.config==5.2.0 oslo.context==2.19.2 oslo.db==4.27.0 oslo.i18n==3.15.3 oslo.log==3.36.0 oslo.messaging==5.29.0 oslo.middleware==3.31.0 oslo.policy==1.30.0 oslo.privsep==1.23.0 oslo.reports==1.18.0 oslo.rootwrap==5.8.0 oslo.serialization==2.18.0 oslo.service==1.24.0 oslo.utils==3.33.0 oslo.versionedobjects==1.31.2 oslotest==3.2.0 osprofiler==1.4.0 ovs==2.8.0 ovsdbapp==0.10.0 paramiko==2.0.0 Paste==2.0.2 PasteDeploy==1.5.0 pbr==2.0.0 pecan==1.0.0 pep8==1.5.7 pika==0.10.0 pika-pool==0.1.3 positional==1.2.1 prettytable==0.7.2 psutil==3.2.2 pyasn1==0.1.8 pycadf==1.1.0 pycodestyle==2.3.1 pycparser==2.18 pyflakes==0.8.1 Pygments==2.2.0 pyinotify==0.9.6 pyparsing==2.1.0 pyperclip==1.5.27 pyroute2==0.4.21 python-dateutil==2.5.3 python-designateclient==2.7.0 python-editor==1.0.3 python-keystoneclient==3.8.0 python-mimeparse==1.6.0 python-neutronclient==6.7.0 python-novaclient==9.1.0 python-subunit==1.0.0 pytz==2013.6 PyYAML==3.12 reno==2.5.0 repoze.lru==0.7 requests==2.14.2 requestsexceptions==1.2.0 rfc3986==0.3.1 Routes==2.3.1 simplejson==3.5.1 six==1.10.0 snowballstemmer==1.2.1 Sphinx==1.6.5 sphinxcontrib-svg2pdfconverter==0.1.0 # BSD sphinxcontrib-websupport==1.0.1 SQLAlchemy==1.2.0 sqlalchemy-migrate==0.11.0 sqlparse==0.2.2 statsd==3.2.1 stestr==1.0.0 stevedore==1.20.0 tempest==17.1.0 Tempita==0.5.2 tenacity==3.2.1 testrepository==0.0.18 testresources==2.0.0 testscenarios==0.4 testtools==2.2.0 tinyrpc==0.6 traceback2==1.4.0 unittest2==1.1.0 urllib3==1.21.1 vine==1.1.4 waitress==1.1.0 WebOb==1.7.1 WebTest==2.0.27 wrapt==1.7.0 neutron-dynamic-routing-16.0.0/AUTHORS0000664000175000017500000001764313656750703017520 0ustar zuulzuul00000000000000Aaron Rosen Aaron Rosen Abhishek Raut Abhishek Raut Adelina Tuvenie Adit Sarfaty Akihiro MOTOKI Akihiro Motoki Akihiro Motoki Andre Pech Andreas Jaeger Andreas Scheuring Angela Smith Ann Kamyshnikova Armando Migliaccio Arvind Somya Assaf Muller AvnishPal Benoît Knecht Bernard Cafarelli Bob Kukura Bob Melander Boden R Brandon Logan Brian Haley Brian Haley Brian Haley Carl Baldwin Carl Baldwin Carol Bouchard Cedric Brandily Chandan Kumar ChangBo Guo(gcb) Chuck Short ChuckC Clark Boylan Claudiu Belu Corey Bryant Cyril Roelandt Dan Florea Dan Wendlandt Darek Smigiel (dasm) Davanum Srinivas David Shaughnessy Dirk Mueller Dongcan Ye Doug Hellmann Doug Wiegley Elena Ezhova Eugene Nikanorov Fawad Khaliq Federico Ressi Flavio Percoco Gary Kotton Gary Kotton Ghanshyam Mann Guoqiang Ding Hareesh Puthalath He Jie Xu Hemanth Ravi Henry Gessau Henry Gessau Henry Gessau Hirofumi Ichihara Hong Hui Xiao Ian Wienand Ignacio Scopetta Ihar Hrachyshka Irena Berezovsky Isaku Yamahata Ivar Lazzaro Jakub Libosvar James Arendt James E. Blair James E. Blair James Page Jens Harbott Jens Rosenboom Jeremy Stanley John Davidge John Perkins John Schwarz Julien Danjou Kanzhe Jiang Kevin Benton Kevin Benton Kevin L. Mitchell Kobi Samoray Kris Lindgren Kyle Mestery Kyle Mestery LIU Yulong LiuNanke Luke Gorrie Luong Anh Tuan Mark McClain Mark McClain Martin Hickey Maru Newby Mate Lakat Mathieu Gagné Matt Riedemann Michael Krotscheck Miguel Angel Ajo Miguel Lavalle Mike Bayer Mike Dorman Mike Kolesnik Mohammad Banikazemi Monty Taylor Moshe Levi Motohiro OTSUKA Murali Birru Na Nachi Ueno Nachi Ueno Nader Lahouti Nate Johnston Nguyen Phuong An Nir Magnezi Numan Siddique Oleg Bondarev Omer Anson OpenStack Release Bot Paul Carver Pavel Bondar Praneet Bachheti Pritesh Kothari Ramanjaneya Rich Curran Robert Collins Roey Chen Romil Gupta Russell Bryant Ryan Tidwell Ryan Tidwell Ryan Tidwell Saksham Varma Salvatore Salvatore Orlando Samer Deeb Sandhya Dasu Sascha Peilicke Sascha Peilicke Sean McGinnis Shih-Hao Li Shiv Haris Shweta P Slawek Kaplonski Somik Behera Sreekumar S Sripriya Stephen Ma Sukhdev Sukhdev Kapur Sumit Naiksatam Swaminathan Vasudevan Swapnil Kulkarni (coolsvap) Sylvain Afchain Sławek Kapłoński Terry Wilson Thierry Carrez Thomas Morin Tobias Urdin Tom Holtzen Trinath Somanchi TrinathSomanchi Tuan Do Anh Victor Laza Vieri <15050873171@163.com> Vivekanandan Narasimhan Vlad Gridin YAMAMOTO Takashi YAMAMOTO Takashi Yalei Wang Yong Sheng Gong Yushiro FURUKAWA Zang MingJie ZhaoBo ZhiQiang Fan abhishek60014726 armando-migliaccio armando-migliaccio asarfaty chenghuiyu fujioka yuuichi fumihiko kakuma gengchc2 ghanshyam gong yong sheng gongysh gujin ji-xuepeng justin ljj loooosy malei mamtap mark mcclain mathieu-rohon melissaml niusmallnan pengyuesheng qinchunhua qingszhao ronak shihanzhang snaiksat steve.ruan sukhdev trinaths unknown vikram.choudhary yuyangbj zengfagao zhangyanxian zhaojingjing0067370 zhulingjie zoushilin Édouard Thuleau neutron-dynamic-routing-16.0.0/babel.cfg0000664000175000017500000000002113656750631020154 0ustar zuulzuul00000000000000[python: **.py] neutron-dynamic-routing-16.0.0/CONTRIBUTING.rst0000664000175000017500000000030713656750631021076 0ustar zuulzuul00000000000000Please see the Neutron CONTRIBUTING.rst file for how to contribute to neutron-dynamic-routing: `Neutron CONTRIBUTING.rst `_ neutron-dynamic-routing-16.0.0/tools/0000775000175000017500000000000013656750704017576 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/tools/generate_config_file_samples.sh0000775000175000017500000000144013656750631025775 0ustar zuulzuul00000000000000#!/bin/sh # # 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. set -e GEN_CMD=oslo-config-generator if ! type "$GEN_CMD" > /dev/null; then echo "ERROR: $GEN_CMD not installed on the system." exit 1 fi for file in `ls etc/oslo-config-generator/*`; do $GEN_CMD --config-file=$file done set -x neutron-dynamic-routing-16.0.0/tools/clean.sh0000775000175000017500000000030413656750631021213 0ustar zuulzuul00000000000000#!/usr/bin/env bash rm -rf ./*.deb ./*.tar.gz ./*.dsc ./*.changes rm -rf */*.deb rm -rf ./plugins/**/build/ ./plugins/**/dist rm -rf ./plugins/**/lib/neutron_*_plugin.egg-info ./plugins/neutron-* neutron-dynamic-routing-16.0.0/tools/check_unit_test_structure.sh0000775000175000017500000000312013656750631025423 0ustar zuulzuul00000000000000#!/usr/bin/env bash # This script identifies the unit test modules that do not correspond # directly with a module in the code tree. See TESTING.rst for the # intended structure. neutron_path=$(cd "$(dirname "$0")/.." && pwd) base_test_path=neutron_dynamic_routing/tests/unit test_path=$neutron_path/$base_test_path test_files=$(find ${test_path} -iname 'test_*.py') ignore_regexes=( "^plugins.*$" ) error_count=0 ignore_count=0 total_count=0 for test_file in ${test_files[@]}; do relative_path=${test_file#$test_path/} expected_path=$(dirname $neutron_path/neutron_dynamic_routing/$relative_path) test_filename=$(basename "$test_file") expected_filename=${test_filename#test_} # Module filename (e.g. foo/bar.py -> foo/test_bar.py) filename=$expected_path/$expected_filename # Package dir (e.g. foo/ -> test_foo.py) package_dir=${filename%.py} if [ ! -f "$filename" ] && [ ! -d "$package_dir" ]; then for ignore_regex in ${ignore_regexes[@]}; do if [[ "$relative_path" =~ $ignore_regex ]]; then ((ignore_count++)) continue 2 fi done echo "Unexpected test file: $base_test_path/$relative_path" ((error_count++)) fi ((total_count++)) done if [ "$ignore_count" -ne 0 ]; then echo "$ignore_count unmatched test modules were ignored" fi if [ "$error_count" -eq 0 ]; then echo 'Success! All test modules match targets in the code tree.' exit 0 else echo "Failure! $error_count of $total_count test modules do not match targets in the code tree." exit 1 fi neutron-dynamic-routing-16.0.0/README.rst0000664000175000017500000000161513656750631020127 0ustar zuulzuul00000000000000Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/neutron-dynamic-routing.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on This package contains neutron-dynamic-routing code which depends upon neutron and it's related libraries to run. Project Resources ================= The homepage for Neutron is: https://launchpad.net/neutron. Use this site for asking for help, and filing bugs. We use a single launchpad page for all Neutron projects. Code is available on opendev.org at: https://opendev.org/openstack/neutron-dynamic-routing Refer to Neutron documentation for more information: `Neutron README.rst `_ Release notes for the project can be found at: https://docs.openstack.org/releasenotes/neutron-dynamic-routing/ neutron-dynamic-routing-16.0.0/.zuul.yaml0000664000175000017500000000136613656750631020404 0ustar zuulzuul00000000000000- job: name: neutron-dynamic-routing-functional parent: neutron-functional vars: project_name: neutron-dynamic-routing-functional - project: templates: - check-requirements - openstack-cover-jobs-neutron - openstack-lower-constraints-jobs-neutron - openstack-python3-ussuri-jobs-neutron - publish-openstack-docs-pti - release-notes-jobs-python3 check: jobs: - neutron-dynamic-routing-functional - neutron-tempest-plugin-dynamic-routing gate: jobs: - neutron-dynamic-routing-functional - neutron-tempest-plugin-dynamic-routing periodic: jobs: - legacy-periodic-neutron-dynamic-routing-dsvm-tempest-with-ryu-master-scenario-ipv4 neutron-dynamic-routing-16.0.0/setup.py0000664000175000017500000000127113656750631020150 0ustar zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # 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 setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) neutron-dynamic-routing-16.0.0/.stestr.conf0000664000175000017500000000012513656750631020704 0ustar zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./neutron_dynamic_routing/tests/unit} top_dir=./ neutron-dynamic-routing-16.0.0/devstack/0000775000175000017500000000000013656750704020242 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/devstack/settings0000664000175000017500000000460713656750631022033 0ustar zuulzuul00000000000000######################### # Devstack Settings # ######################### # Each service you enable has the following meaning: # q-dr - Add this config flag for Openstack Neutron server node # q-dr-agent - Add this config flag indicate that dynamic routing agent # will be running # This can be overridden in the localrc file DR_MODE=${DR_MODE:-allinone} # DR_MODE is used to configure how devstack works with neutron-dynamic-routing. # You can configure it in there ways: # # DR_MODE=allinone # Use this mode if you want to run neutron server and q-dr-agent on same node. # Useful for a single node deployment or on the control node of a multi-node # devstack environment. # # DR_MODE=dr_plugin # Use this to enable dr plugin extension on neutron server # # DR_MODE=dr_agent # Use this for the nodes where you want to run q-dr-agent in a multi-node # devstack environment. case $DR_MODE in allinone) if is_neutron_legacy_enabled; then enable_service q-dr q-dr-agent else enable_service neutron-dr neutron-dr-agent fi ;; dr_plugin) if is_neutron_legacy_enabled; then enable_service q-dr else enable_service neutron-dr fi ;; dr_agent) if is_neutron_legacy_enabled; then enable_service q-dr-agent else enable_service neutron-dr-agent fi ;; esac # DR_SUPPORTED_PROTOCOLS specifies the list of protocols supported # by neutron-dynamic-routing project. ONLY BGP is supported as of now # and it's enabled by default. The protocols may include: "BGP OSPF ISIS RIP". # It can be overridden in the localrc file. DR_SUPPORTED_PROTOCOLS=${DR_SUPPORTED_PROTOCOLS:-"BGP"} ####################### # Binary Settings # ####################### NEUTRON_DYNAMIC_ROUTING_DIR=$DEST/neutron-dynamic-routing DR_AGENT_BINARY=${DR_AGENT_BINARY:-"$NEUTRON_BIN_DIR/neutron-bgp-dragent"} ################################ # Protocol Config Settings # ################################ ########### # BGP # ########### DR_AGENT_BGP_CONF_FILE=${DR_AGENT_BGP_CONF_FILE:-"$NEUTRON_CONF_DIR/bgp_dragent.ini"} BGP_ROUTER_ID=${BGP_ROUTER_ID:-"127.0.0.1"} BGP_PLUGIN=${BGP_PLUGIN:-"neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin"} OSKEN_BGP_SPEAKER_DRIVER="neutron_dynamic_routing.services.bgp.agent.driver.os_ken.driver.OsKenBgpDriver" neutron-dynamic-routing-16.0.0/devstack/plugin.sh0000664000175000017500000000114013656750631022067 0ustar zuulzuul00000000000000LIBDIR=$NEUTRON_DYNAMIC_ROUTING_DIR/devstack/lib source $LIBDIR/dr if [[ "$1" == "stack" ]]; then case "$2" in install) echo_summary "Installing neutron-dynamic-routing" dr_install ;; post-config) echo_summary "Configuring neutron-dynamic-routing" dr_post_configure ;; extra) echo_summary "Launching neutron-dynamic-routing agent" start_dr_agent ;; esac elif [[ "$1" == "unstack" ]]; then echo_summary "Uninstalling neutron-dynamic-routing" stop_dr_agent fi neutron-dynamic-routing-16.0.0/devstack/lib/0000775000175000017500000000000013656750704021010 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/devstack/lib/dr0000664000175000017500000000562213656750631021344 0ustar zuulzuul00000000000000function is_protocol_enabled { local enabled=1 local protocol=$1 for temp in $DR_SUPPORTED_PROTOCOLS ;do if [ $protocol == $temp ] ; then enabled=0 fi done return $enabled } ############################## # BGP Section # ############################## function configure_dr_agent_bgp_config { cp $NEUTRON_DYNAMIC_ROUTING_DIR/etc/bgp_dragent.ini.sample $DR_AGENT_BGP_CONF_FILE iniset $DR_AGENT_BGP_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL iniset $DR_AGENT_BGP_CONF_FILE bgp bgp_router_id $BGP_ROUTER_ID } function configure_dr_agent_bgp_driver { if [ -z "$BGP_SPEAKER_DRIVER" ] ; then BGP_SPEAKER_DRIVER=$OSKEN_BGP_SPEAKER_DRIVER fi iniset $DR_AGENT_BGP_CONF_FILE bgp bgp_speaker_driver $BGP_SPEAKER_DRIVER } function configure_dr_agent_scheduler_driver { if [ -n "$BGP_SCHEDULER_DRIVER" ] ; then iniset $NEUTRON_CONF DEFAULT bgp_drscheduler_driver $BGP_SCHEDULER_DRIVER fi } ############################# # Stack Install Section # ############################# #This API will be called for phase "install" function dr_install { setup_develop $NEUTRON_DYNAMIC_ROUTING_DIR } ############################# # Stack Post-config Section # ############################# #This API will be called for phase "post-config" function dr_generate_config_files { (cd $NEUTRON_DYNAMIC_ROUTING_DIR && exec ./tools/generate_config_file_samples.sh) } function dr_post_configure { if is_service_enabled q-dr neutron-dr && is_service_enabled q-svc neutron-api; then if is_protocol_enabled BGP; then neutron_service_plugin_class_add $BGP_PLUGIN fi fi if is_service_enabled q-dr-agent neutron-dr-agent; then dr_generate_config_files if is_protocol_enabled BGP; then configure_dr_agent_bgp_config configure_dr_agent_bgp_driver configure_dr_agent_scheduler_driver fi fi } ############################# # Stack Extra Section # ############################# #This API will be called for phase "extra" function start_dr_agent { local process="$DR_AGENT_BINARY --config-file $NEUTRON_CONF " local bgp_parameter if is_protocol_enabled BGP; then bgp_parameter="--config-file $DR_AGENT_BGP_CONF_FILE" fi agent_process=$process$bgp_parameter if is_neutron_legacy_enabled; then if is_service_enabled q-dr-agent; then run_process q-dr-agent "$agent_process" fi else if is_service_enabled neutron-dr-agent; then run_process neutron-dr-agent "$agent_process" fi fi } ############################# # Unstack Section # ############################# #This API will be called for unstack function stop_dr_agent { if is_neutron_legacy_enabled; then stop_process q-dr-agent else stop_process neutron-dr-agent fi } neutron-dynamic-routing-16.0.0/devstack/README.rst0000664000175000017500000000234713656750631021736 0ustar zuulzuul00000000000000====================== Enabling in Devstack ====================== 1. Download devstack:: git clone https://opendev.org/openstack/devstack.git 2. Add neutron-dynamic-routing to devstack. The minimal set of critical local.conf additions are following:: cd devstack cat << EOF > local.conf > [[local|localrc]] > enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing > EOF 3. run devstack:: ./stack.sh Notes: 1. In the default case, neutron-dynamic-routing is installed in allinone mode. In multiple nodes environment, for controller node:: cd devstack cat << EOF > local.conf > [[local|localrc]] > enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing > DR_MODE=dr_plugin > EOF For the nodes where you want to run dr-agent:: cd devstack cat << EOF > local.conf > [[local|localrc]] > enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing > DR_MODE=dr_agent > EOF 2. In the default case, protocol BGP is enabled for neutron-dynamic-routing. You can change "DR_SUPPORTED_PROTOCOLS" in "devstack/settings" to protocols wanted. neutron-dynamic-routing-16.0.0/LICENSE0000664000175000017500000002363713656750631017455 0ustar zuulzuul00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. neutron-dynamic-routing-16.0.0/playbooks/0000775000175000017500000000000013656750703020440 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv4/0000775000175000017500000000000013656750704032351 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv4/run.yaml0000664000175000017500000000444213656750631034044 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dynamic-routing-dsvm-tempest-scenario-ipv4 from old job gate-neutron-dynamic-routing-dsvm-tempest-scenario-ipv4-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export DEVSTACK_GATE_USE_PYTHON3=True export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_dynamic_routing.tests.tempest.scenario.ipv4\." export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export PROJECTS="openstack/neutron-dynamic-routing $PROJECTS" export PROJECTS="openstack/neutron-tempest-plugin $PROJECTS" DEVSTACK_LOCAL_CONFIG="NEUTRON_CREATE_INITIAL_NETWORKS=False" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-tempest-plugin https://opendev.org/openstack/neutron-tempest-plugin" export DEVSTACK_LOCAL_CONFIG function gate_hook { bash -xe $BASE/new/neutron-dynamic-routing/neutron_dynamic_routing/tests/contrib/gate_hook.sh dsvm-scenario } export -f gate_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv4/post.yamlneutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv4/post.yam0000664000175000017500000000455113656750631034052 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv6/0000775000175000017500000000000013656750704032353 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv6/run.yaml0000664000175000017500000000444213656750631034046 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dynamic-routing-dsvm-tempest-scenario-ipv6 from old job gate-neutron-dynamic-routing-dsvm-tempest-scenario-ipv6-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export DEVSTACK_GATE_USE_PYTHON3=True export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_dynamic_routing.tests.tempest.scenario.ipv6\." export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export PROJECTS="openstack/neutron-tempest-plugin $PROJECTS" export PROJECTS="openstack/neutron-dynamic-routing $PROJECTS" DEVSTACK_LOCAL_CONFIG="NEUTRON_CREATE_INITIAL_NETWORKS=False" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-tempest-plugin https://opendev.org/openstack/neutron-tempest-plugin" export DEVSTACK_LOCAL_CONFIG function gate_hook { bash -xe $BASE/new/neutron-dynamic-routing/neutron_dynamic_routing/tests/contrib/gate_hook.sh dsvm-scenario } export -f gate_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv6/post.yamlneutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-ipv6/post.yam0000664000175000017500000000455113656750631034054 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-api/0000775000175000017500000000000013656750704030437 5ustar zuulzuul00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-api/run.yaml0000664000175000017500000000514713656750631032135 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dynamic-routing-dsvm-tempest-api from old job gate-neutron-dynamic-routing-dsvm-tempest-api tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing enable_plugin neutron-tempest-plugin https://opendev.org/openstack/neutron-tempest-plugin EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export DEVSTACK_GATE_USE_PYTHON3=True export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 if [ "ZUUL_BRANCH" == "stable/newton" ]; then export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_dynamic_routing\." else export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_dynamic_routing.tests.tempest.api\." fi export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export PROJECTS="openstack/neutron-dynamic-routing $PROJECTS" export PROJECTS="openstack/neutron-tempest-plugin $PROJECTS" if [ "ZUUL_BRANCH" != "stable/newton" ]; then function gate_hook { bash -xe $BASE/new/neutron-dynamic-routing/neutron_dynamic_routing/tests/contrib/gate_hook.sh dsvm-api } export -f gate_hook fi cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-api/post.yaml0000664000175000017500000000455113656750631032314 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/0000775000175000017500000000000013656750704032550 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/run.yamlneutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/run.yam0000664000175000017500000000470313656750631034067 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dynamic-routing-dsvm-tempest-scenario-basic from old job gate-neutron-dynamic-routing-dsvm-tempest-scenario-basic-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export DEVSTACK_GATE_USE_PYTHON3=True export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_dynamic_routing.tests.tempest.scenario.basic\." export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export PROJECTS="openstack/neutron-dynamic-routing $PROJECTS" export PROJECTS="openstack/neutron-tempest-plugin $PROJECTS" DEVSTACK_LOCAL_CONFIG="NEUTRON_CREATE_INITIAL_NETWORKS=False" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-tempest-plugin https://opendev.org/openstack/neutron-tempest-plugin" export DEVSTACK_LOCAL_CONFIG # NOTE(frickler): Some tests are failing when running in parallel, likely due collisions in the docker setup export TEMPEST_CONCURRENCY=1 function gate_hook { bash -xe $BASE/new/neutron-dynamic-routing/neutron_dynamic_routing/tests/contrib/gate_hook.sh dsvm-scenario } export -f gate_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/post.yamlneutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-scenario-basic/post.ya0000664000175000017500000000455113656750631034074 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs ././@LongLink0000000000000000000000000000016000000000000011212 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4/neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-sce0000775000175000017500000000000013656750704034015 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000017000000000000011213 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4/run.yamlneutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-sce0000664000175000017500000000541513656750631034023 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4 from old job gate-neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4-nv tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export DEVSTACK_GATE_USE_PYTHON3=True export PYTHONUNBUFFERED=true export DEVSTACK_GATE_TEMPEST=1 export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_dynamic_routing.tests.tempest.scenario.ipv4\." export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [[ "$BRANCH_OVERRIDE" != "default" ]] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi export PROJECTS="openstack/neutron-dynamic-routing $PROJECTS" export PROJECTS="openstack/neutron-tempest-plugin $PROJECTS" DEVSTACK_LOCAL_CONFIG="NEUTRON_CREATE_INITIAL_NETWORKS=False" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing" DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron-tempest-plugin https://opendev.org/openstack/neutron-tempest-plugin" if [[ "ipv4" == "basic" ]]; then DEVSTACK_LOCAL_CONFIG+=$'\n'"BGP_SCHEDULER_DRIVER=neutron_dynamic_routing.services.bgp.scheduler.bgp_dragent_scheduler.ChanceScheduler" fi export DEVSTACK_LOCAL_CONFIG function gate_hook { local os_ken_path=$BASE/new/os_ken_master if [[ ! -d $os_ken_path ]]; then git clone https://opendev.org/openstack/os-ken $os_ken_path fi sudo pip install -e $os_ken_path bash -xe $BASE/new/neutron-dynamic-routing/neutron_dynamic_routing/tests/contrib/gate_hook.sh dsvm-scenario } export -f gate_hook cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' ././@LongLink0000000000000000000000000000017100000000000011214 Lustar 00000000000000neutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-scenario-ipv4/post.yamlneutron-dynamic-routing-16.0.0/playbooks/neutron-dynamic-routing-dsvm-tempest-with-os-ken-master-sce0000664000175000017500000000455113656750631034023 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*nose_results.html - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testr_results.html.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.testrepository/tmp* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=**/*testrepository.subunit.gz - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}/tox' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/.tox/*/log/* - --include=*/ - --exclude=* - --prune-empty-dirs - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs neutron-dynamic-routing-16.0.0/.coveragerc0000664000175000017500000000017513656750631020561 0ustar zuulzuul00000000000000[run] branch = True source = neutron_dynamic_routing # omit = neutron_dynamic_routing/tests/* [report] ignore_errors = True