networking-bgpvpn-8.0.0/0000775000175100017510000000000013245511747015235 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/0000775000175100017510000000000013245511747017726 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/notes/0000775000175100017510000000000013245511747021056 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/notes/deprecate-old-opencontrail-driver-a598892ddf54c724.yaml0000666000175100017510000000053613245511271032612 0ustar zuulzuul00000000000000--- prelude: > Deprecates old OpenContrail driver deprecations: - | The first OpenContrail Driver was not developed with production ready in mind, it was more a proof of concept. We do not recommend to use it in production. Instead a production ready driver is available in the OpenContrail monolithic Neutron core plugin tree. networking-bgpvpn-8.0.0/releasenotes/notes/odl_router_association-fa2ed7c396531418.yaml0000666000175100017510000000010713245511235030645 0ustar zuulzuul00000000000000--- features: - OpenDaylight driver now supports Router associations networking-bgpvpn-8.0.0/releasenotes/notes/filtering-on-resource-association-2acdbc5b59d1a40a.yaml0000666000175100017510000000020113245511235033075 0ustar zuulzuul00000000000000--- features: - The API now supports filtering BGPVPN resources based on the networks or routers they are associated with. networking-bgpvpn-8.0.0/releasenotes/notes/horizon-support-06a7b21286002949.yaml0000777000175100017510000000120213245511271027044 0ustar zuulzuul00000000000000--- prelude: > New Horizon panels for BGPVPN resources, allowing you to create a bgpvpn and to associate related resources such as a network or a router. features: - | Horizon: * a view of all the existing BGPVPNs. * ability to view details of a BGPVPN. * ability to create, update and delete BGPVPN resources for an admin user. * ability to update BGPVPN resources for a tenant user. (with restrictions, compared to what an admin user can change) * abiity to associate/disassociate BGPVPN to/from networks and routers (for both tenant and admin users) networking-bgpvpn-8.0.0/releasenotes/notes/bagpipe-driver-improvements-401a7ba59a6f5f45.yaml0000666000175100017510000000065713245511235031606 0ustar zuulzuul00000000000000--- features: - BaGPipe driver improvement for a clean integration in the Neutron OpenVSwitch agent (see Bug `1492021 `_). Instead of requiring to use a modified OVS agent, we now provide an extension that is loaded into the unmodified OVS agent. fixes: - with BaGPipe driver, the OVS agent does not lose BGPVPN flows on restart (Bug `1531459 `_) networking-bgpvpn-8.0.0/releasenotes/notes/pre_commit_checks-b902ee19a3654a7b.yaml0000666000175100017510000000017713245511235027632 0ustar zuulzuul00000000000000--- features: - Pre-commit hooks were added in the driver framework, and then leveraged in BaGPipe and OpenDaylight drivers networking-bgpvpn-8.0.0/releasenotes/notes/bagpipe-router-compat-b53b6f3799cd23db.yaml0000666000175100017510000000100613245511235030442 0ustar zuulzuul00000000000000--- prelude: > The ovs/bagpipe driver now let you use both a Neutron router and a BGPVPN association simultaneously on a given Port. features: - The bagpipe driver now let happily coexist a BGPVPN association and a Neutron router. Traffic that does not match any VPN route will be handled by the Neutron router. This evolution depends on corresponding evolutions in networking-bagpipe and bagpipe-bgp. (`see bug 1627645 `_) networking-bgpvpn-8.0.0/releasenotes/notes/mitaka-prelude-1675467c144a91ea.yaml0000666000175100017510000000025313245511235026716 0ustar zuulzuul00000000000000--- prelude: > Mitaka release is a short-cycle release to compensate for the delayed Liberty release and get the project in sync with Openstack release cycles networking-bgpvpn-8.0.0/releasenotes/notes/bgpvpn_service_declaration-6d9ecd2c397e4821.yaml0000666000175100017510000000034313245511235031545 0ustar zuulzuul00000000000000features: - | The BGPVPN Interconnection API can now be enabled by adding ``bgpvpn`` to ``service_plugins`` in ``neutron.conf``, instead of the verbose ``networking_bgpvpn.neutron.services.plugin.BGPVPNPlugin``. networking-bgpvpn-8.0.0/releasenotes/notes/0_heat-support-ab233de7401aeb36.yaml0000666000175100017510000000011013245511235027054 0ustar zuulzuul00000000000000--- features: - Heat support for the whole BGPVPN Interconnection API networking-bgpvpn-8.0.0/releasenotes/notes/add-vni-to-bgpvpn-31d6eda7ba6d5047.yaml0000666000175100017510000000021513245511235027457 0ustar zuulzuul00000000000000--- features: - | Add ``vni`` optional attribute to ``bgpvpn`` resource to control the VXLAN VNI when VXLAN encapsulation is used. networking-bgpvpn-8.0.0/releasenotes/notes/.placeholder0000666000175100017510000000000013245511235023321 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/notes/bagpipe_enable_evpn-ae64f77df89e069b.yaml0000666000175100017510000000021113245511271030215 0ustar zuulzuul00000000000000--- features: - | BGPVPNs of type L2 are now supported with Neutron ML2 reference drivers (linuxbridge, OVS work in progress). networking-bgpvpn-8.0.0/releasenotes/source/0000775000175100017510000000000013245511747021226 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/newton.rst0000666000175100017510000000022313245511235023261 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: stable/newton networking-bgpvpn-8.0.0/releasenotes/source/_static/0000775000175100017510000000000013245511747022654 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/_static/.placeholder0000666000175100017510000000000013245511235025117 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/liberty.rst0000666000175100017510000000022213245511235023420 0ustar zuulzuul00000000000000============================== Liberty Series Release Notes ============================== .. release-notes:: :branch: origin/stable/liberty networking-bgpvpn-8.0.0/releasenotes/source/pike.rst0000666000175100017510000000021713245511235022702 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike networking-bgpvpn-8.0.0/releasenotes/source/conf.py0000666000175100017510000002160213245511235022520 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. # Networking-bgpvpn Release Notes documentation build configuration file # # 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 = 'openstack/networking-bgpvpn' bug_project = 'bgpvpn' bug_tag = '' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Networking-bgpvpn Release Notes' copyright = u'2016, Networking-bgpvpn 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 = '%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 = 'NetworkingBgpvpnReleaseNotesdoc' # -- 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', 'NetworkingBgpvpnReleaseNotes.tex', u'Networking-bgpvpn Release Notes Documentation', u'Networking-bgpvpn 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', 'networkingbgpvpnreleasenotes', u'Networking-bgpvpn Release Notes Documentation', [u'Networking-bgpvpn 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', 'NetworkingBgpvpnReleaseNotes', u'Networking-bgpvpn Release Notes Documentation', u'Networking-bgpvpn Developers', 'NetworkingBgpvpnReleaseNotes', '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/'] networking-bgpvpn-8.0.0/releasenotes/source/locale/0000775000175100017510000000000013245511747022465 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/locale/en_GB/0000775000175100017510000000000013245511747023437 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175100017510000000000013245511747025224 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000666000175100017510000001423213245511271030251 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: Networking-bgpvpn Release Notes\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-12-19 20:14+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-12-12 08:36+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "3.0.1-6" msgstr "3.0.1-6" msgid "4.0.0" msgstr "4.0.0" msgid "5.0.0" msgstr "5.0.0" msgid "6.0.0" msgstr "6.0.0" msgid "7.0.0" msgstr "7.0.0" msgid "8.0.0.0b1" msgstr "8.0.0.0b1" msgid "" "BGPVPNs of type L2 are now supported with Neutron ML2 reference drivers " "(linuxbridge, OVS work in progress)." msgstr "" "BGPVPNs of type L2 are now supported with Neutron ML2 reference drivers " "(linuxbridge, OVS work in progress)." msgid "" "BaGPipe driver improvement for a clean integration in the Neutron " "OpenVSwitch agent (see Bug `1492021 `_). " "Instead of requiring to use a modified OVS agent, we now provide an " "extension that is loaded into the unmodified OVS agent." msgstr "" "BaGPipe driver improvement for a clean integration in the Neutron " "OpenVSwitch agent (see Bug `1492021 `_). " "Instead of requiring to use a modified OVS agent, we now provide an " "extension that is loaded into the unmodified OVS agent." msgid "Bug Fixes" msgstr "Bug Fixes" msgid "Current Series Release Notes" msgstr "Current Series Release Notes" msgid "Heat support for the whole BGPVPN Interconnection API" msgstr "Heat support for the whole BGPVPN Interconnection API" msgid "Horizon:" msgstr "Horizon:" msgid "Liberty Series Release Notes" msgstr "Liberty Series Release Notes" msgid "Mitaka Series Release Notes" msgstr "Mitaka Series Release Notes" msgid "" "Mitaka release is a short-cycle release to compensate for the delayed " "Liberty release and get the project in sync with Openstack release cycles" msgstr "" "Mitaka release is a short-cycle release to compensate for the delayed " "Liberty release and get the project in sync with OpenStack release cycles" msgid "Networking-bgpvpn Release Notes" msgstr "Networking-bgpvpn Release Notes" msgid "New Features" msgstr "New Features" msgid "" "New Horizon panels for BGPVPN resources, allowing you to create a bgpvpn and " "to associate related resources such as a network or a router." msgstr "" "New Horizon panels for BGPVPN resources, allowing you to create a BGPVPN and " "to associate related resources such as a network or a router." msgid "Newton Series Release Notes" msgstr "Newton Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "OpenDaylight driver now supports Router associations" msgstr "OpenDaylight driver now supports Router associations" msgid "Other Notes" msgstr "Other Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "" "Pre-commit hooks were added in the driver framework, and then leveraged in " "BaGPipe and OpenDaylight drivers" msgstr "" "Pre-commit hooks were added in the driver framework, and then leveraged in " "BaGPipe and OpenDaylight drivers" msgid "Prelude" msgstr "Prelude" msgid "Start using reno to manage release notes." msgstr "Start using Reno to manage release notes." msgid "" "The API now supports filtering BGPVPN resources based on the networks or " "routers they are associated with." msgstr "" "The API now supports filtering BGPVPN resources based on the networks or " "routers they are associated with." msgid "" "The BGPVPN Interconnection API can now be enabled by adding ``bgpvpn`` to " "``service_plugins`` in ``neutron.conf``, instead of the verbose " "``networking_bgpvpn.neutron.services.plugin.BGPVPNPlugin``." msgstr "" "The BGPVPN Interconnection API can now be enabled by adding ``bgpvpn`` to " "``service_plugins`` in ``neutron.conf``, instead of the verbose " "``networking_bgpvpn.neutron.services.plugin.BGPVPNPlugin``." msgid "" "The bagpipe driver now let happily coexist a BGPVPN association and a " "Neutron router. Traffic that does not match any VPN route will be handled by " "the Neutron router. This evolution depends on corresponding evolutions in " "networking-bagpipe and bagpipe-bgp. (`see bug 1627645 `_)" msgstr "" "The bagpipe driver now let happily coexist a BGPVPN association and a " "Neutron router. Traffic that does not match any VPN route will be handled by " "the Neutron router. This evolution depends on corresponding evolutions in " "networking-bagpipe and bagpipe-bgp. (`see bug 1627645 `_)" msgid "" "The ovs/bagpipe driver now let you use both a Neutron router and a BGPVPN " "association simultaneously on a given Port." msgstr "" "The ovs/bagpipe driver now let you use both a Neutron router and a BGPVPN " "association simultaneously on a given Port." msgid "a view of all the existing BGPVPNs." msgstr "a view of all the existing BGPVPNs." msgid "" "abiity to associate/disassociate BGPVPN to/from networks and routers (for " "both tenant and admin users)" msgstr "" "ability to associate/disassociate BGPVPN to/from networks and routers (for " "both tenant and admin users)" msgid "" "ability to create, update and delete BGPVPN resources for an admin user." msgstr "" "ability to create, update and delete BGPVPN resources for an admin user." msgid "" "ability to update BGPVPN resources for a tenant user. (with restrictions, " "compared to what an admin user can change)" msgstr "" "ability to update BGPVPN resources for a tenant user. (with restrictions, " "compared to what an admin user can change)" msgid "ability to view details of a BGPVPN." msgstr "ability to view details of a BGPVPN." msgid "" "with BaGPipe driver, the OVS agent does not lose BGPVPN flows on restart " "(Bug `1531459 `_)" msgstr "" "with BaGPipe driver, the OVS agent does not lose BGPVPN flows on restart " "(Bug `1531459 `_)" networking-bgpvpn-8.0.0/releasenotes/source/unreleased.rst0000666000175100017510000000015313245511235024100 0ustar zuulzuul00000000000000============================ Current Series Release Notes ============================ .. release-notes:: networking-bgpvpn-8.0.0/releasenotes/source/index.rst0000666000175100017510000000031213245511271023055 0ustar zuulzuul00000000000000================================ Networking-bgpvpn Release Notes ================================ .. toctree:: :maxdepth: 1 unreleased mitaka pike ocata newton mitaka liberty networking-bgpvpn-8.0.0/releasenotes/source/mitaka.rst0000666000175100017510000000022313245511235023215 0ustar zuulzuul00000000000000=================================== Mitaka Series Release Notes =================================== .. release-notes:: :branch: stable/mitaka networking-bgpvpn-8.0.0/releasenotes/source/ocata.rst0000666000175100017510000000023013245511235023034 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata networking-bgpvpn-8.0.0/releasenotes/source/_templates/0000775000175100017510000000000013245511747023363 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/releasenotes/source/_templates/.placeholder0000666000175100017510000000000013245511235025626 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/0000775000175100017510000000000013245511747016002 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/0000775000175100017510000000000013245511747017302 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/configuration/0000775000175100017510000000000013245511747022151 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/configuration/networking-bgpvpn.rst0000666000175100017510000000132113245511235026353 0ustar zuulzuul00000000000000====================== networking-bgpvpn.conf ====================== To use networking-bgpvpn, you need to configure one of valid service providers for ``BGPVPN`` service in ``service_provider`` of ``[service_providers]`` group of the neutron server. Note that you can specify multiple providers for BGPVPN but only one of them can be default. * Dummy provider: ``service_provider = BGPVPN:Dummy:networking_bgpvpn.neutron.services.service_drivers.driver_api.BGPVPNDriver:default`` * BaGPipe provider: ``service_provider = BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default`` .. show-options:: :config-file: etc/oslo-config-generator/networking-bgpvpn.conf networking-bgpvpn-8.0.0/doc/source/configuration/index.rst0000666000175100017510000000114713245511235024007 0ustar zuulzuul00000000000000===================== Configuration Options ===================== This section provides a list of all possible options for each configuration file. These are generated from code and reflect the current state of code in the networking-bgpvpn repository. Configuration Reference ----------------------- networking-bgpvpn uses the following configuration files for its various services. .. toctree:: :glob: :maxdepth: 1 * Sample Configuration Files -------------------------- The following are sample configuration files for all networking-bgpvpn. .. toctree:: :glob: :maxdepth: 1 samples/* networking-bgpvpn-8.0.0/doc/source/configuration/samples/0000775000175100017510000000000013245511747023615 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/configuration/samples/networking-bgpvpn.rst0000666000175100017510000000045613245511235030027 0ustar zuulzuul00000000000000============================= Sample networking-bgpvpn.conf ============================= This sample configuration can also be viewed in `the raw format <../../_static/config-samples/networking-bgpvpn.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/networking-bgpvpn.conf.sample networking-bgpvpn-8.0.0/doc/source/configuration/samples/opencontrail-driver.rst0000666000175100017510000000047013245511235030330 0ustar zuulzuul00000000000000=============================== Sample opencontrail-driver.conf =============================== This sample configuration can also be viewed in `the raw format <../../_static/config-samples/opencontrail-driver.conf.sample>`_. .. literalinclude:: ../../_static/config-samples/opencontrail-driver.conf.sample networking-bgpvpn-8.0.0/doc/source/configuration/opencontrail-driver.rst0000666000175100017510000000024213245511235026661 0ustar zuulzuul00000000000000======================== opencontrail-driver.conf ======================== .. show-options:: :config-file: etc/oslo-config-generator/opencontrail-driver.conf networking-bgpvpn-8.0.0/doc/source/user/0000775000175100017510000000000013245511747020260 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/user/usage.rst0000666000175100017510000001326113245511235022113 0ustar zuulzuul00000000000000======== Usage ======== Use from OpenStack CLI ------------------------ Example commands to use by the admin to create a BGPVPN resource: .. code-block:: console openstack bgpvpn create --route-target 64512:1 --project b954279e1e064dc9b8264474cb3e6bd2 openstack bgpvpn list openstack bgpvpn set --name myBGPVPN Example commands to use by the tenant owning the BGPVPN to associate a Network to it: .. code-block:: console openstack bgpvpn network association create myBGPVPN # returns openstack bgpvpn network association list myBGPVPN openstack bgpvpn network association show myBGPVPN openstack bgpvpn network association delete myBGPVPN There are more details in the `OpenStack Client (OSC) documentation for BGPVPN `_. Use from Horizon ---------------- See :doc:`horizon`. Use from Heat ------------- See :doc:`heat`. Use from Python --------------- The BGPVPN API is dynamically loaded by Neutron. There is the same behaviour with the Python lib "neutronclient" use. This allows to programmatically handle BGPVPN resources as well as Network association resources and Router association resources. Methods ~~~~~~~ BGPVPN Resources ^^^^^^^^^^^^^^^^ .. csv-table:: API methods for BGPVPN resources :header: Method Name,Description,Input parameter(s),Output "list_bgpvpns()", "Get the list of defined BGPVPN resources for the current tenant. An optional list of BGPVPN parameters can be used as filter.", "1. Use \**kwargs as filter, e.g. list_bgpvpn(param1=val1, param2=val2,...) (Optional)", "Dictionary of BGPVPN attributes" "create_bgpvpn()", "Create a BGPVPN resource for the current tenant. Extra information about the BGPVPN resource can be provided as input.", "1. Dictionary of BGPVPN attributes (Optional)", "Dictionary of BGPVPN attributes" "show_bgpvpn()", "Get all information for a given BGPVPN.", "1. UUID of the said BGPVPN", "Dictionary of BGPVPN attributes related to the BGPVPN provided as input" "update_bgpvpn()", "Update the BGPVPN resource with the parameters provided as input.", "1. UUID of the said BGPVPN 2. Dictionary of BGPVPN attributes to be updated", "Dictionary of BGPVPN attributes" "delete_bgpvpn()", "Delete a given BGPVPN resource of which the UUID is provided as input.", "1. UUID of the said BGPVPN", "Boolean" Network Association Resources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. csv-table:: API methods for Network association resources :header: Method Name,Description,Input parameter(s),Output "list_network_associations()", "Get the list of defined NETWORK ASSOCIATION resources for a given BGPVPN. An optional list of NETWORK ASSOCIATION parameters can be used as filter.", "1. UUID of the BGPVPN 2. Use \**kwargs as filter, e.g. list_network_associations( BGPVPN UUID, param1=val1, param2=val2,...) (Optional)", "List of dictionaries of NETWORK ASSOCIATION attributes, one of each related to a given BGPVPN" "create_network_association()", "Create a NETWORK ASSOCIATION resource for a given BGPVPN. Network UUID must be defined, provided in a NETWORK ASSOCIATION resource as input parameter.", "1. UUID of the said BGPVPN 2. Dictionary of NETWORK ASSOCIATION parameters", "Dictionary of NETWORK ASSOCIATION attributes" "show_network_association()", "Get all parameters for a given NETWORK ASSOCIATION.", "1. UUID of the NETWORK ASSOCIATION resource 2. UUID of the BGPVPN resource", "Dictionary of NETWORK ASSOCIATION parameters" "update_network_association()", "Update the parameters of the NETWORK ASSOCIATION resource provided as input.", "1. UUID of the NETWORK ASSOCIATION resource 2. UUID of the BGPVPN resource 3. Dictionary of NETWORK ASSOCIATION parameters", "Dictionary of NETWORK ASSOCIATION parameters" "delete_network_association()", "Delete a given NETWORK ASSOCIATION resource of which the UUID is provided as input.", "1. UUID of the NETWORK ASSOCIATION resource 2. UUID of the BGPVPN resource", "Boolean" Router Association Resources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. csv-table:: API methods for Router associations :header: Method Name,Description,Input parameter(s),Output "list_router_associations()", "Get the list of defined ROUTER ASSOCIATION resources for a given BGPVPN. An optional list of ROUTER ASSOCIATION parameters can be used as filter", "1. UUID of the BGPVPN 2. Use \**kwargs as filter, e.g. list_router_associations( BGPVPN UUID, param1=val1, param2=val2,...) (Optional)", "List of dictionaries of ROUTER ASSOCIATION attributes, one of each related to a given BGPVPN" "create_router_association()", "Create a ROUTER ASSOCIATION resource for a given BGPVPN UUID. Router UUID must be defined, provided in a ROUTER ASSOCIATION resource as input parameter.", "1. UUID of the said BGPVPN 2. Dictionary of ROUTER ASSOCIATION parameters (Optional)", "Dictionary of ROUTER ASSOCIATION parameters" "show_router_association()", "Get all parameters for a given ROUTER ASSOCIATION.", "1. UUID of the ROUTER ASSOCIATION resource 2. UUID of the BGPVPN resource", "Dictionary of ROUTER ASSOCIATION parameters" "update_router_association()", "Update the parameters of the ROUTER ASSOCIATION resource provided as input.", "1. UUID of the ROUTER ASSOCIATION resource 2. UUID of the BGPVPN resource 3. Dictionary of ROUTER ASSOCIATION parameters", "Dictionary of ROUTER ASSOCIATION parameters" "delete_router_association()", "Delete a given ROUTER ASSOCIATION resource.", "1. UUID of the ROUTER ASSOCIATION resource 2. UUID of the BGPVPN resource", "Boolean" Examples ~~~~~~~~ BGPVPN + Network Association Resources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: ../samples/bgpvpn-sample01.py networking-bgpvpn-8.0.0/doc/source/user/components-sdn.blockdiag0000666000175100017510000000255713245511235025073 0ustar zuulzuul00000000000000blockdiag components-sdn { span_width = 64; node_height = 100; shadow_style=none; default_shape = roundedbox; group bgpvpn { label="BGPVPN service plugin"; color=red; api[label="BGPVPN API"]; db[shape=flowchart.database,label="DB"]; driver; } group backend_g { label="Backend"; color=orange; comment[label="can be e.g.\nan 'SDN' solution...",shape=note,color=orange,style=none]; backend[label="...",shape=box,stacked,color=none]; vswitches[stacked,label="vswitches\nand/or routers"]; bgpspeakers[stacked,label="MP-BGP Speakers"]; } group routers { color=lightgrey; shape=line; style=dashed; bgppeers[label="BGP Peers",stacked,color=green]; mplsrouters[label="MPLS routers"]; bgppeers -- mplsrouters[style=dotted,folded]; } admin_or_tenant [shape=actor,label="admin, tenant"]; admin_or_tenant -> api[color=blue]; api -> driver ; api -> db[folded]; driver -> db[folded]; driver -> backend; backend <-> bgpspeakers; bgpspeakers <-> bgppeers[color=green,label="MP-BGP",textcolor=green]; backend -> vswitches[folded]; vswitches <-> mplsrouters[label="MPLS\nor ..",folded]; } networking-bgpvpn-8.0.0/doc/source/user/workflows.seqdiag0000666000175100017510000000423513245511235023652 0ustar zuulzuul00000000000000diagram { span_width = 40; os-admin [label="Openstack Admin",color="lightgray"]; tenant [label="Openstack Tenant X",color="lightblue"]; api [label="Neutron BGPVPN API",color="red"]; driver [label="BGPVPN Driver for\nbackend Foo",color="red"]; backend [label="BGPVPN Backend Foo\n",color="orange"]; bgppeers [label="BGP Peers",color="green"]; backend --> bgppeers[label="BGP peerings",color=green]; backend <-- bgppeers[rightnote="BGP sessions live in parallel\nto BGPVPN service plugin",color=green]; os-admin -> api [label="POST: create a BGP VPN\nresource corresponding to a\nBGP VPN",color=blue]; api -> driver [leftnote="persist resource"]; driver --> backend [label="(driver-backend exchanges\nvarying bw. backends)"]; driver <-- backend; api <-- driver; os-admin <-- api [label="BGPVPN Y",color=blue]; os-admin -> api [label="UPDATE: set tenant X as\nowner of BGPVPN Y",color=blue]; api -> driver; driver --> backend [label="(?)"]; driver <-- backend; api <-- driver; os-admin <-- api; tenant -> api [label="GET:Learns that it\nowns BGPVPN Y",color=blue]; api -> driver; driver --> backend [label="(?)"]; driver <-- backend; api <-- driver; tenant <-- api; tenant -> api [label="UPDATE:Associate BGPVPN Y to\nnetwork Z",color=blue]; api -> driver; driver -> backend [rightnote="now ready to interconnect\nNetwork Z and BGPVPN Y"]; driver <-- backend; backend -> bgppeers[label="MP-BGP VPNv4 routes\ntoward Network Z exported\nto BGP VPN Y",color=green]; backend <-- bgppeers[label="MP-BGP VPNv4 routes\nfrom BGP VPN Y prefixes", leftnote="forwarding plane setup\n(e.g. MPLS/GRE)",color=green]; api <-- driver; } networking-bgpvpn-8.0.0/doc/source/user/overview.rst0000666000175100017510000001236413245511271022660 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode ========================================== BGP VPN Interconnection Service Overview ========================================== Introduction ------------ BGP-based IP VPNs networks are widely used in the industry especially for enterprises. This project aims at supporting inter-connection between L3VPNs and Neutron resources, i.e. Networks, Routers and Ports. A typical use-case is the following: a tenant already having a BGP IP VPN (a set of external sites) setup outside the datacenter, and they want to be able to trigger the establishment of connectivity between VMs and these VPN sites. Another similar need is when E-VPN is used to provide an Ethernet interconnect between multiple sites. This service plugin exposes an API to interconnect OpenStack Neutron ports, typically VMs, via the Networks and Routers they are connected to, with an L3VPN network as defined by [RFC4364]_ (BGP/MPLS IP Virtual Private Networks). The framework is generic to also support E-VPN [RFC7432]_, which inherits the same protocol architecture as BGP/MPLS IP VPNs. Alternatives and related techniques ----------------------------------- Other techniques are available to build VPNs, but the goal of this proposal is to make it possible to create the interconnect we need when the technology of BGP-based VPNs is already used outside an Openstack cloud. Reminder on BGP VPNs and Route Targets -------------------------------------- BGP-based VPNs allow a network operator to offer a VPN service to a VPN customer, delivering isolating connectivity between multiple sites of this customer. Contrarily to for instance IPSec or SSL-based VPNs, these VPNs are not typically built over the Internet, are most often not encrypted, and their creation is not at the hand of the end-user. Here is a reminder on how the connectivity is defined between sites of a VPN (VRFs). In BGP-based VPNs, a set of identifiers called Route Targets are associated with a VPN, and in the typical case identify a VPN ; they can also be used to build other VPN topologies such as hub'n'spoke. Each VRF (Virtual Routing and Forwarding) in a PE (Provider Edge router) imports/exports routes from/to Route Targets. If a VRF imports from a Route Target, BGP IP VPN routes will be imported in this VRF. If a VRF exports to a Route Target, the routes in the VRF will be associated to this Route Target and announced by BGP. Mapping between PEs/CEs and Neutron constructs ---------------------------------------------- As outlined in the overview, how PEs, CEs (Customer Edge router), VRFs map to Neutron constructs will depend on the backend driver used for this service plugin. For instance, with the current bagpipe driver, the PE and VRF functions are implemented on compute nodes and the VMs are acting as CEs. This PE function will BGP-peer with edge IP/MPLS routers, BGP Route Reflectors or other PEs. Bagpipe BGP which implements this function could also be instantiated in network nodes, at the l3agent level, with a BGP speaker on each l3agent; router namespaces could then be considered as CEs. Other backends might want to consider the router as a CE and drive an external PE to peer with the service provider PE, based on information received with this API. It's up to the backend to manage the connection between the CE and the cloud provider PE. Another typical option is where the driver delegates the work to an SDN controller which drives a BGP implementation advertising/consuming the relevant BGP routes and remotely drives the vswitches to setup the datapath accordingly. API and Workflows ----------------- BGP VPN are deployed, and managed by the operator, in particular to manage Route Target identifiers that control the isolation between the different VPNs. Because of this BGP VPN parameters cannot be chosen by tenants, but only by the admin. In addition, network operators may prefer to not expose actual Route Target values to the users. The operation that is let at the hand of a tenant is the association of a BGPVPN resource that it owns with his Neutron Networks or Routers. So there are two workflows, one for the admin, one for a tenant. * Admin/Operator Workflow: Creation of a BGPVPN * the cloud/network admin creates a BGPVPN for a tenant based on contract and OSS information about the VPN for this tenant * at this stage, the list of associated Networks and Routers can be empty * Tenant Workflow: Association of a BGPVPN to Networks and/or Routers, on-demand * the tenant lists the BGPVPNs that it can use * the tenant associates a BGPVPN with one or more Networks or Routers. Sequence diagram summarizing these two workflows: .. seqdiag:: workflows.seqdiag Component architecture overview ------------------------------- This diagram gives an overview of the architecture: .. blockdiag:: components-sdn.blockdiag This second diagram depicts how the *bagpipe* reference driver implements its backend: .. blockdiag:: drivers/bagpipe/overview.blockdiag References ---------- .. [RFC4364] BGP/MPLS IP Virtual Private Networks (IP VPNs) http://tools.ietf.org/html/rfc4364 .. [RFC7432] BGP MPLS-Based Ethernet VPN (Ethernet VPNs, a.k.a E-VPN) http://tools.ietf.org/html/rfc7432 networking-bgpvpn-8.0.0/doc/source/user/heat.rst0000666000175100017510000001075613245511235021736 0ustar zuulzuul00000000000000==== Heat ==== Installation and Configuration ============================== Devstack will automatically configure heat to support BGPVPN. Other deployments need to add the directory for the python networking_bgpvpn_heat module to ``plugin_dirs`` in the heat config: ``/etc/heat/heat.conf``. This directory can be found out with: .. code-block:: console dirname $(python -c "import networking_bgpvpn_heat as n;print(n.__file__)") Examples ======== Heat Orchestration Template (HOT) example 1 ------------------------------------------- This template has to be run with admin rights and will create a BGPVPN for the current tenant, along with a Network associated with it: .. literalinclude:: ../../../networking_bgpvpn_heat/examples/bgpvpn_test-00.yaml :language: yaml In devstack, this HOT file can be used with cloud admin privileges in the demo project; such privileges can be obtained with the command: .. code-block:: console source openrc admin demo This example can then be run: .. code-block:: console $ heat stack-create networks -f bgpvpn_test-00.yaml +--------------------------------------+------------+--------------------+---------------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +--------------------------------------+------------+--------------------+---------------------+--------------+ | 5a6c2bf1-c5da-4f8f-9838-4c3e59d13d41 | networks | CREATE_IN_PROGRESS | 2016-03-02T08:32:52 | None | +--------------------------------------+------------+--------------------+---------------------+--------------+ $ heat stack-list +--------------------------------------+------------+-----------------+---------------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +--------------------------------------+------------+-----------------+---------------------+--------------+ | 5a6c2bf1-c5da-4f8f-9838-4c3e59d13d41 | networks | CREATE_COMPLETE | 2016-03-02T08:32:52 | None | +--------------------------------------+------------+-----------------+---------------------+--------------+ Heat Orchestration Template (HOT) example 2 ------------------------------------------- This is a set of two templates: * one that has to be run with admin rights and will create a BGPVPN for the 'demo' tenant: .. literalinclude:: ../../../networking_bgpvpn_heat/examples/bgpvpn_test-04-admin.yaml :language: yaml .. code-block:: console $ source openrc admin admin $ heat stack-create bgpvpn -f bgpvpn_test-04-admin.yaml * one to run as a plain 'demo' tenant user, that will: * create a Network and bind it to the 'default_vpn' BGPVPN * create a second Network connected to a Router, and bind the Router to the 'default_vpn' .. literalinclude:: ../../../networking_bgpvpn_heat/examples/bgpvpn_test-04-tenant.yaml :language: yaml .. code-block:: console $ source openrc demo demo $ heat stack-create networks_bgpvpn -f bgpvpn_test-04-tenant.yaml +--------------------------------------+-----------------+--------------------+---------------------+--------------+ | id | stack_name | stack_status | creation_time | updated_time | +--------------------------------------+-----------------+--------------------+---------------------+--------------+ | a3cf1c1b-ac6c-425c-a4b5-d8ca894539f2 | networks_bgpvpn | CREATE_IN_PROGRESS | 2016-03-02T09:16:39 | None | +--------------------------------------+-----------------+--------------------+---------------------+--------------+ $ openstack bgpvpn list +--------------------------------------+-------------+------+-------------------------------------------+------------------------------------------------+ | id | name | type | networks | routers | +--------------------------------------+-------------+------+-------------------------------------------+------------------------------------------------+ | 473e5218-f4a2-46bd-8086-36d6849ecf8e | default VPN | l3 | [u'5b1af75b-0608-4e03-aac1-2608728be45d'] | [u'cb9c7304-e844-447d-88e9-4a0a2dc14d21'] | +--------------------------------------+-------------+------+-------------------------------------------+------------------------------------------------+ networking-bgpvpn-8.0.0/doc/source/user/api.rst0000666000175100017510000000461113245511235021557 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode === API === This API is documented in the `Neutron API Reference `_. ADMIN ===== Configuration ============= On VXLAN VNI ------------ .. note:: This feature is under development in the Queens release VXLAN is one option among others that could be used for BGP E-VPNs. When VXLAN is used on a hardware platform the use of a locally-assigned id may not be always possible which introduces the need to configure a globally-assigned VXLAN VNI. The optional ``vni`` attribute is an admin-only parameter and allows the admin to enforce the use of a chosen globally-assigned VXLAN VNI for the said BGPVPN. The default when no VNI is specified and the VXLAN encapsulation is used, is to let the backend choose the VNI in advertised routes, and use the VNI in received routes for transmitted traffic. The backend will conform to E-VPN overlay specs. If the ``vni`` attribute is set for a BGPVPN, the following is enforced: * the routes announced by the backend will advertise the specified VNI (this relates to traffic sent from this BGP VPN to a Network or Router) * for the routes received by the backend for this BGPVPN, and that carry a different VNI that the VNI specified for the BGPVPN the behavior may depend on the backend, with the recommended behavior being to liberally accept such routes. If a backend does not support the approach recommended above of liberally accepting routes with a different VNI, the check can be implemented as follows: * when a route is imported, for each BGPVPN associated to the Network or Router and having a VNI defined: * the set of Route Targets of the route is intersected with the import_rts of the BGPVPN * if this intersection is non-empty the ``vni`` of the BGPVPN is retained * the route is used to establish connectivity to the destination in the forwarding plane only if the advertised VNI is equal to all retained VNIs in the previous step The above check is applied similarly for a Router associated to multiple BGP VPN. The backend is expected to provide troubleshooting information for the cases when a route ends up not being used because the VNI check failed. Valid range for the ``vni`` attribute is [1, 2\ :sup:`24`\ -1]. networking-bgpvpn-8.0.0/doc/source/user/horizon.rst0000666000175100017510000000436613245511235022505 0ustar zuulzuul00000000000000======== Horizon ======== General information =================== Networking-bgpvpn contains the bgpvpn_dashboard plugin for Horizon. It adds a BGPVPN Interconnections panel in the admin section. Admin users can handle BGPVPNs resources through this panel. The operations possible for admin users are: * listing BGPVPN * creating a BGPVPN * editing a BGPVPN * associating or disassociating a BGPVPN to network(s) * associating or disassociating a BGPVPN to router(s) * deleting a BGPVPN For non admin users the plugin adds a BGPVPN Interconnections panel in the Project section under the Network subsection. The operations possible for non admin users are: * listing BGPVPN (display only name, type, networks and routers associations) * editing a BGPVPN (only the name) * associating or disassociating a BGPVPN to network(s) * associating or disassociating a BGPVPN to router(s) Installation and Configuration ============================== Devstack will automatically configure Horizon to enable the Horizon plugin. For others deployments we assume that Horizon and networking-bgpvpn are already installed. Their installation folders are respectively and . Copy configuration file: .. code-block:: shell cp /bgpvpn_dashboard/enabled/_[0-9]*.py /openstack_dashboard/local/enabled/ Configure the policy file for BGPVPN dashboard in OpenStack Dashboard ``local_settings.py``. ```` is a directory which contains configurations for BGPVPN dashboard and the location varies across distributions or deployments. ```` can be found with: ``dirname $(python -c 'import bgpvpn_dashboard as _; print _.__file__')`` .. code-block:: python POLICY_FILES[' networking-bgpvpn'] = '/bgpvpn_dashboard/etc/bgpvpn-horizon.conf' .. note:: If you do not configure ``POLICY_FILES`` in your ``local_settings.py``, you also need to define the default ``POLICY_FILES`` in ``local_settings.py``. If you use the example ``local_settings.py`` file from horizon, what you need is to uncomment ``POLICY_FILES`` (which contains the default values). Restart the web server hosting Horizon. The BGPVPN Interconnections panels will now be in your Horizon dashboard. networking-bgpvpn-8.0.0/doc/source/user/index.rst0000666000175100017510000000023013245511235022106 0ustar zuulzuul00000000000000================== User Documentation ================== .. toctree:: :maxdepth: 2 overview drivers/index usage horizon heat api networking-bgpvpn-8.0.0/doc/source/user/drivers/0000775000175100017510000000000013245511747021736 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/user/drivers/opendaylight/0000775000175100017510000000000013245511747024425 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/user/drivers/opendaylight/index.rst0000666000175100017510000000207213245511235026261 0ustar zuulzuul00000000000000=================== OpenDaylight driver =================== The **OpenDaylight** driver for the BGPVPN service plugin is designed to work jointly with the `OpenDaylight SDN controller `__. OpenDaylight driver requires `networking-odl plugin`_ which comes with its own devstack scripts. Details on how to configure devstack for OpenDaylight plugin can be found at `networking-odl/devstack`_. * add the following to local.conf to enable networking-odl plugin: .. code-block:: none enable_plugin networking-odl http://git.openstack.org/openstack/networking-odl * add the following to local.conf to enable ODL Driver for BGPVPN service Plugin: .. code-block:: ini NETWORKING_BGPVPN_DRIVER="BGPVPN:OpenDaylight:networking_bgpvpn.neutron.services.service_drivers.opendaylight.odl.OpenDaylightBgpvpnDriver:default" * Run stack.sh: .. code-block:: console ./stack.sh .. _networking-odl plugin : https://launchpad.net/networking-odl .. _networking-odl/devstack : https://github.com/openstack/networking-odl/tree/master/devstack networking-bgpvpn-8.0.0/doc/source/user/drivers/bagpipe/0000775000175100017510000000000013245511747023345 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/user/drivers/bagpipe/overview.blockdiag0000666000175100017510000000313413245511235027047 0ustar zuulzuul00000000000000blockdiag components-bagpipe { span_width = 64; node_height = 100; shadow_style=none; default_shape = roundedbox; group bgpvpn { label="BGPVPN service plugin"; color=red; api[label="BGPVPN API"]; db[shape=flowchart.database,label="Neutron DB"]; driver[label="bagpipe driver"]; api -> driver ; api -> db[folded]; driver -> db[folded]; } group backend_g { label="bagpipe backend"; color=orange; comment[label="on each compute\nnode ... : ",shape=note,color=orange,style=none]; backend[label="OpenVSwitch Agent\n+ BGPVPN extension",color=grey,textcolor=darkorange]; vswitch[label="OVS br-int/br-tun",color=lightgrey]; mplsvswitch[label="OVS br-mpls",color="darkorange"]; bgpspeaker[label="bagpipe-bgp",color="darkorange"]; backend -> bgpspeaker; backend -> vswitch[folded]; vswitch <-> mplsvswitch[label="packets"]; bgpspeaker -> mplsvswitch[folded]; } group routers { color=lightgrey; shape=line; style=dashed; bgppeers[label="BGP Peers",stacked,color=green]; mplsrouters[label="MPLS routers"]; bgppeers -- mplsrouters[style=dotted,folded]; } admin_or_tenant [shape=actor,label="admin, tenant"]; admin_or_tenant -> api[color=blue]; driver <-> backend [label="RPCs"]; bgpspeaker <-> bgppeers[color=green,label="MP-BGP",textcolor=green]; mplsvswitch <-> mplsrouters[label="MPLS\nor ..",folded]; } networking-bgpvpn-8.0.0/doc/source/user/drivers/bagpipe/index.rst0000666000175100017510000001177113245511271025207 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode ================== OVS/BaGPipe driver ================== Introduction ------------ The **BaGPipe** driver for the BGPVPN service plugin is designed to work jointly with the openvswitch ML2 mechanism driver. It relies on the use of the bagpipe-bgp BGP VPN implementation on compute nodes and the MPLS implementation in OpenVSwitch. Architecture overview --------------------- The bagpipe driver for the BGPVPN service plugin interacts with the openvswitch agent on each compute node, which is extended to support new RPCs to trigger the local configuration on compute nodes of BGP VPN instances and of their MPLS dataplane. .. blockdiag:: overview.blockdiag Limitations ----------- On DHCP ports, Router interface ports, external network ports, etc. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ No connectivity will be setup with BGP VPNs for DHCP ports or Router interface ports, or other network specific ports. This improves the load on network nodes by avoiding them to import/export a significant amount of routes, without limiting BGP VPN deployment scenarios because no useful traffic would be exchanged between a router or DHCP interface of a network associated to a BGP VPN. Similarly, the driver will not bind a port on an external network. This behavior will be revisited once a use case is well identified. bagpipe_v2 driver ----------------- For Queens release, the mechanism used by this driver for RPCs was changed. The v1 driver ``networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver`` is backwards compatible with pre-Queens neutron agents and can be used during a rolling upgrade, e.g. from Pike to Queens. The v2 driver ``networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe_v2.BaGPipeBGPVPNDriver`` does not produce the old RPCs anymore and can be used: * on a greenfield deployment * after an upgrade * during a non-rolling upgrade (some BGPVPN operations would be disrupted during the time where pre-Queens agent still run) Future developments may happen only on the v2 driver and the v1 driver will be ultimately abandoned. How to use ? ------------ The steps to take to use this driver are generally: * install the networking-bagpipe package on both control nodes and compute nodes * on control node, configure neutron to use bagpipe driver * on compute nodes, configure the neutron agent to use bagpipe_bgpvpn extension and configure bagpipe-bgp Of course, the typical way is to have all this taken care of by an automated Openstack installer. In devstack ~~~~~~~~~~~ * follow the instruction in README.rst * ``local.conf``: * add the following to enable the BaGPipe driver for the BGPVPN service plugin: .. code-block:: ini NETWORKING_BGPVPN_DRIVER="BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe_v2.BaGPipeBGPVPNDriver:default" * enable networking-bagpipe_, which contains code for agent extensions: .. code-block:: ini enable_plugin networking-bagpipe git://git.openstack.org/openstack/networking-bagpipe.git # enable_plugin networking-bagpipe git://git.openstack.org/openstack/networking-bagpipe.git stable/pike # enable_plugin networking-bagpipe git://git.openstack.org/openstack/networking-bagpipe.git stable/queens * on a control node, if you want to run the Fake Route-Reflector there (relevant only for a multinode setup): .. code-block:: none enable_service b-fakerr * on compute nodes: * the compute node Neutron agent is the Neutron openvswitch agent, with the ``bagpipe_bgpvpn`` agent extension: * install networking-bagpipe_ (the code to interact with ``bagpipe-bgp`` comes from there): .. code-block:: ini enable_plugin networking-bagpipe git://git.openstack.org/openstack/networking-bagpipe.git # enable_plugin networking-bagpipe git://git.openstack.org/openstack/networking-bagpipe.git stable/pike # enable_plugin networking-bagpipe git://git.openstack.org/openstack/networking-bagpipe.git stable/queens * the ``bagpipe_bgpvpn`` agent extension is automatically added to the agent configuration by the devstack plugin * bagpipe-bgp will be installed automatically (part of networking-bagpipe since Pike, or as a submodule before) * you need to enable and configure bagpipe-bgp, typically with a peering to a BGP Route-Reflector or BGP router(s): .. code-block:: ini enable_service b-bgp BAGPIPE_DATAPLANE_DRIVER_IPVPN=mpls_ovs_dataplane.MPLSOVSDataplaneDriver # BAGPIPE_DATAPLANE_DRIVER_IPVPN=ovs # simpler config available > after Ocata # IP of your route-reflector or BGP router, or fakeRR # BAGPIPE_BGP_PEERS defaults to $SERVICE_HOST, which will point to the controller in a # multi-node devstack setup #BAGPIPE_BGP_PEERS=1.2.3.4,2.3.4.5 .. _networking-bagpipe: https://docs.openstack.org/networking-bagpipe/latest/ networking-bgpvpn-8.0.0/doc/source/user/drivers/opencontrail/0000775000175100017510000000000013245511747024433 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/user/drivers/opencontrail/index.rst0000666000175100017510000000702513245511235026272 0ustar zuulzuul00000000000000=================== OpenContrail driver =================== Introduction ------------ The **OpenContrail** driver for the BGPVPN service plugin is designed to work jointly with the `OpenContrail SDN controller`_ (`GitHub`_). The BGP VPN driver can be found in the `monolithic Neutron plugin tree`__ [#]_. .. Warning:: The `old OpenContail driver`_ has been deprecated in Queens release in favor of the production ready `driver`_ and plan to be completly removed in Rocky release. Be careful, **no** migration path is planned. Limitations ----------- Route Distinguishers ~~~~~~~~~~~~~~~~~~~~ The OpenContrail driver for the BGPVPN service plugin does not permit specifying `route distinguisher`_. Resource Association ~~~~~~~~~~~~~~~~~~~~ The OpenContrail driver for the BGPVPN service plugin does not yet support `association with ports`_. But it supports `network associations`_ and `router associations`_. VPN Type ~~~~~~~~ The OpenContrail driver for the BGPVPN service plugin can create L2 & L3 VPN types for network associations and L3 VPN type for router association. How to use ? ------------ On an Openstack Installation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ [TBC (package installation + config)] In devstack ~~~~~~~~~~~ A `devstack plugin`_ can be used to setup an OpenContrail dev/test platform. * Clone devstack: .. code-block:: console git clone git@github.com:openstack-dev/devstack * Here a proposed devstack ``local.conf`` file which permits to deploy OpenStack keystone, glance, nova, neutron/networking-bgpvpn and compile/install all OpenContrail services and dependencies: .. code-block:: bash [[local|localrc]] LOG=True LOGDAYS=1 PASSWORD="secret" DATABASE_PASSWORD=$PASSWORD RABBIT_PASSWORD=$PASSWORD SERVICE_TOKEN=$PASSWORD SERVICE_PASSWORD=$PASSWORD ADMIN_PASSWORD=$PASSWORD # disable some nova services disable_service n-obj n-novnc n-cauth # disable cinder disable_service cinder c-api c-vol c-sch # disable heat disable_service h-eng h-api h-api-cfn h-api-cw # diable horizon disable_service horizon # disable swift disable_service swift s-proxy s-object s-container s-account # disable some contrail services #disable_service ui-webs ui-jobs named dns query-engine DEST=/opt/stack/openstack CONTRAIL_DEST=/opt/stack/contrail enable_plugin contrail https://github.com/zioc/contrail-devstack-plugin.git enable_plugin networking-bgpvpn git://git.openstack.org/openstack/networking-bgpvpn.git NETWORKING_BGPVPN_DRIVER="BGPVPN:OpenContrail:neutron_plugin_contrail.plugins.opencontrail.networking_bgpvpn.contrail.ContrailBGPVPNDriver:default" .. [#] That driver requires OpenContrail release upper or equal to 4.0 .. _OpenContrail SDN controller: http://www.opencontrail.org/ .. _GitHub: https://github.com/Juniper/contrail-controller .. _driver: https://github.com/Juniper/contrail-neutron-plugin/tree/master/neutron_plugin_contrail/plugins/opencontrail/networking_bgpvpn __ driver_ .. _old OpenContail driver: https://github.com/openstack/networking-bgpvpn/tree/stable/queens/networking_bgpvpn/neutron/services/service_drivers/opencontrail .. _route distinguisher: https://developer.openstack.org/api-ref/networking/v2/#on-route-distinguishers-rds .. _router associations: https://developer.openstack.org/api-ref/networking/v2/#router-associations .. _network associations: https://developer.openstack.org/api-ref/networking/v2/#network-associations .. _association with ports: https://developer.openstack.org/api-ref/network/v2/#port-associations .. _devstack plugin: https://github.com/zioc/contrail-devstack-plugin networking-bgpvpn-8.0.0/doc/source/user/drivers/nuage/0000775000175100017510000000000013245511747023035 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/user/drivers/nuage/index.rst0000666000175100017510000000143213245511235024670 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode ===================== Nuage Networks driver ===================== The Nuage Network driver works jointly with `Nuage Networks VSP `__. A pre-requisite for the nuage BGPVPN driver is that the Nuage-specific installation and configuration steps have been applied; in particular the installation of the ``nuage_neutron`` package. Please refer to Nuage Networks documentation. The driver will be enabled, by specifying in ``/etc/neutron/networking_bgpvpn.conf``: .. code-block:: ini [service_providers] service_provider = BGPVPN:Nuage:nuage_neutron.bgpvpn.services.service_drivers.driver.NuageBGPVPNDriver:default networking-bgpvpn-8.0.0/doc/source/user/drivers/index.rst0000666000175100017510000000654613245511271023604 0ustar zuulzuul00000000000000======= Drivers ======= The BGPVPN service plugin supports the following drivers: .. toctree:: :maxdepth: 1 bagpipe/index opencontrail/index opendaylight/index nuage/index The API is consistent across drivers, but not all drivers support all parts of the API. Refer to the Driver Compatibility Matrix to determine what is supported with each driver. Driver Compatibility Matrix --------------------------- +-------------------------------------------+---------------------------------------------------+ | API | Driver | +---------------------+---------------------+-----------+---------------+--------------+--------+ | Object | Attribute | Neutron | OpenContrail | OpenDaylight | Nuage | | | | w. bagpipe| | | | | | +-------+---+-------+-------+-------+------+ | | | | ovs |lnx|v1 [#]_| v2 | v1 | v2 | | +=====================+=====================+=======+===+=======+=======+=======+======+========+ | bgpvpn | base object | Yes | Yes | Yes | Yes | Yes | Yes | | +---------------------+-----------+-------+-------+-------+------+--------+ | | route_distinguisher | No | No | No | Yes | Yes | Yes | | +---------------------+-----------+-------+-------+-------+------+--------+ | | route_targets | Yes | Yes | Yes | Yes | Yes | Yes | | +---------------------+-----------+-------+-------+-------+------+--------+ | | import_targets | Yes | Yes | Yes | Yes | Yes | Yes | | +---------------------+-----------+-------+-------+-------+------+--------+ | | export_targets | Yes | Yes | Yes | Yes | Yes | Yes | | +-------+-------------+-------+---+-------+-------+-------+------+--------+ | | | L3 | Yes |Yes| Yes | Yes | Yes | Yes | Yes | | | type +-------------+-------+---+-------+-------+-------+------+--------+ | | | L2 |No [#]_|Yes| No | Yes | No | ? | ? | +---------------------+-------+-------------+-------+---+-------+-------+-------+------+--------+ | network_association | base object | Yes | Yes | Yes | Yes | Yes | No | +---------------------+---------------------+-----------+-------+-------+-------+------+--------+ | router_association | base object | Yes | No | Yes | Yes | Yes | Yes | +---------------------+---------------------+-----------+-------+-------+-------+------+--------+ .. [#] OpenContrail driver v1 has been deprecated in favor of the production ready `driver v2`_. Warning, **no** migration path is planned. .. [#] Support for BGPVPNs of type L2 with Neutron/bagpipe is supported with linuxbridge agents. The support for OVS agents is being worked on in the Queens cycle. .. _driver v2: https://github.com/Juniper/contrail-neutron-plugin/tree/master/neutron_plugin_contrail/plugins/opencontrail/networking_bgpvpn networking-bgpvpn-8.0.0/doc/source/_static/0000775000175100017510000000000013245511747020730 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/_static/.placeholder0000666000175100017510000000000013245511235023173 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/conf.py0000666000175100017510000000677613245511235020613 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', 'sphinxcontrib.blockdiag', 'sphinxcontrib.seqdiag', #'sphinx.ext.intersphinx', 'openstackdocstheme', 'oslo_config.sphinxext', 'oslo_config.sphinxconfiggen', ] # openstackdocstheme options repository_name = 'openstack/networking-bgpvpn' bug_project = 'bgpvpn' bug_tag = '' # 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'networking_bgpvpn' 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_static_path = ['_static'] html_theme = 'openstackdocs' html_last_updated_fmt = '%Y-%m-%d %H:%M' # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, u'%s Documentation' % project, u'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} #blockdiag_antialias = True seq_antialias = True seqdiag_html_image_format = "svg" blockdiag_html_image_format = "svg" # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'networking-bgpvpn.conf', 'opencontrail-driver.conf', ] def _get_config_generator_config_definition(conf_file): config_file_path = '../../etc/oslo-config-generator/%s' % conf_file # oslo_config.sphinxconfiggen appends '.conf.sample' to the filename, # strip file extentension (.conf or .ini). output_file_path = '_static/config-samples/%s' % conf_file.rsplit('.', 1)[0] return (config_file_path, output_file_path) config_generator_config_file = [ _get_config_generator_config_definition(conf_file) for conf_file in _config_generator_config_files ] networking-bgpvpn-8.0.0/doc/source/contributor/0000775000175100017510000000000013245511747021654 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/contributor/contributing.rst0000666000175100017510000000011613245511235025105 0ustar zuulzuul00000000000000============ Contributing ============ .. include:: ../../../CONTRIBUTING.rst networking-bgpvpn-8.0.0/doc/source/contributor/index.rst0000666000175100017510000000017613245511235023513 0ustar zuulzuul00000000000000================= Contributor Guide ================= .. toctree:: :maxdepth: 2 contributing specs future/index networking-bgpvpn-8.0.0/doc/source/contributor/future/0000775000175100017510000000000013245511747023166 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/contributor/future/index.rst0000666000175100017510000000012313245511235025015 0ustar zuulzuul00000000000000To be implemented ================= .. toctree:: :maxdepth: 2 attributes networking-bgpvpn-8.0.0/doc/source/contributor/future/attributes.rst0000666000175100017510000000411013245511235026074 0ustar zuulzuul00000000000000================= Future attributes ================= Specifications for the following attributes have been defined but not implemented yet: .. csv-table:: Future attributes :header: Attribute Name,Type,Access,Default Value,Validation/Constraint,Description technique, string, RW admin only, None, for instance "ipvpn" or "evpn", (optional) selection of the technique used to implement the VPN auto_aggregate,bool,RW admin only,False,{ True | False },enable prefix aggregation or not (type l3 only) but no support in any driver admin_state_up,bool,RW admin only,True,{ True | False },interconnection with this BGPVPN is enabled by the admin 'auto_aggregate' attribute ~~~~~~~~~~~~~~~~~~~~~~~~~~ The 'auto_aggregate' flag controls whether or not routes should be automatically aggregated before being advertised outside Neutron. A backend may or may not support this behavior, and its driver should report an API error in the latter case. 'technique' attribute ~~~~~~~~~~~~~~~~~~~~~ The 'technique' attribute is optional and can be used by the admin to select one of multiple techniques when more than one is supported by the driver. When no technique is specified, the driver will use a default value. An API call will be available to let the API user know about the types supported by the driver for a said vpn type. Currently defined techniques are: * for l3: * 'ipvpn': this corresponds to RFC4364 * 'evpn-prefix': this corresponds to draft-ietf-bess-evpn-prefix-advertisement * for l2: * 'evpn': this corresponds to RFC7432 API call to list the available techniques, with example answers: * GET /bgpvpn/techniques: .. code-block:: json { "techniques": { "l3": [ "ipvpn" ], "l2": [ "evpn" ] } } * GET /bgpvpn/techniques/l3: .. code-block:: json { "l3": [ "ipvpn"] } * GET /bgpvpn/techniques/l2: .. code-block:: json { "l2": [ "evpn"] } 'admin_state_up' attribute ~~~~~~~~~~~~~~~~~~~~~~~~~~ This is an admin-only attribute allowing the admin to shutdown connectivity to and from a BGP VPN and expose this state to the tenant. networking-bgpvpn-8.0.0/doc/source/contributor/specs.rst0000666000175100017510000000077313245511235023524 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode Specification notes =================== Database design --------------- The implementation will rely on three tables: * one for BGPVPN objects * one to define the n-n relation ship between BGPVPNs and Networks * one to define the n-n relation ship between BGPVPNs and Routers The information stored in these tables will reflect what is exposed on the API. networking-bgpvpn-8.0.0/doc/source/index.rst0000666000175100017510000000257313245511235021144 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode ============================================= Neutron BGP VPN Interconnection Documentation ============================================= Introduction ============ BGP-based IP VPNs networks are widely used in the industry especially for enterprises. This project aims at supporting inter-connection between L3VPNs and Neutron resources, i.e. Networks, Routers and Ports. A typical use-case is the following: a tenant already having a BGP IP VPN (a set of external sites) setup outside the datacenter, and they want to be able to trigger the establishment of connectivity between VMs and these VPN sites. Another similar need is when E-VPN is used to provide an Ethernet interconnect between multiple sites. This service plugin exposes an API to interconnect OpenStack Neutron ports, typically VMs, via the Networks and Routers they are connected to, with an L3VPN network as defined by [RFC4364]_ (BGP/MPLS IP Virtual Private Networks). The framework is generic to also support E-VPN [RFC7432]_, which inherits the same protocol architecture as BGP/MPLS IP VPNs. Contents ======== .. toctree:: :maxdepth: 2 user/index install/index configuration/index contributor/index .. rubric:: Indices and Tables * :ref:`genindex` * :ref:`search` networking-bgpvpn-8.0.0/doc/source/install/0000775000175100017510000000000013245511747020750 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/install/index.rst0000666000175100017510000000644013245511271022607 0ustar zuulzuul00000000000000========================= Install and Configuration ========================= Installation ============ The details related to how a package should be installed may depend on your environment. If possible, you should rely on packages provided by your Linux and/or Openstack distribution. If you use ``pip``, follow these steps to install networking-bgpvpn: * identify the version of the networking-bgpvpn package that matches your Openstack version: * Liberty: most recent of 3.0.x * Mitaka: most recent of 4.0.x * Newton: most recent of 5.0.x * Ocata: most recent of 6.0.x * Pike: most recent of 7.0.x * (see ``_) * indicate pip to (a) install precisely this version and (b) take into account Openstack upper constraints on package versions for dependencies (example for ocata): .. code-block:: console $ pip install -c https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt?h=stable/ocata networking-bgpvpn=6.0.0 Configuration ============= The service plugin is enabled in Neutron, by adding ``bgpvpn`` to the list of enabled service plugins in ``neutron.conf`` (typically in ``/etc/neutron/`` but the location used may depend on your setup or packaging). For instance: .. code-block:: ini service_plugins = router,bgpvpn The BGPVPN driver to use is then specified in the ``networking_bgpvpn.conf`` file (located by default under ``/etc/neutron/``, but in any case in one of the directories specified with ``--config-dir`` at neutron startup, which may differ from ``/etc/neutron`` in your setup): .. code-block:: ini [service_providers] service_provider = BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default #service_provider= BGPVPN:Dummy:networking_bgpvpn.neutron.services.service_drivers.driver_api.BGPVPNDriver:default A given driver may require additional packages to work; the driver section provides detailed installation information for each specific driver. Policy ====== API Policy for the BGPVPN service plugin can be controlled via the standard policy framework. When pip is used to install the package, a default policy file is installed at ``/etc/neutron/policy.d/bgpvpn.conf``. Database setup ============== The DB tables for networking-bgpvpn are created and upgraded with: .. code-block:: console neutron-db-manage --config-file /etc/neutron/neutron.conf --subproject networking-bgpvpn upgrade Devstack ======== You can easily test the bgpvpn service plugin with devstack, by adding the following line to your local.conf: .. code-block:: none enable_plugin networking-bgpvpn git://git.openstack.org/openstack/networking-bgpvpn.git Or the following if you want a specific branch or version (example for Mitaka): .. code-block:: none enable_plugin networking-bgpvpn git://git.openstack.org/openstack/networking-bgpvpn.git stable/mitaka By default, the service driver will use a dummy driver, that only responds to API calls, and stores data in the database. If you want to test a fully functional driver with devstack, you can configure the bagpipe driver with its devstack plugin (see :doc:`/user/drivers/bagpipe/index`). Detailed information on how to use other drivers is provided in the documentation for each of these drivers. networking-bgpvpn-8.0.0/doc/source/samples/0000775000175100017510000000000013245511747020746 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/doc/source/samples/bgpvpn-sample01.py0000666000175100017510000001205113245511235024225 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 neutronclient.v2_0 import client import os import sys # Parameter for subnet neutron object SUBNET_IP = "192.168.24.0/24" # Parameters for bgpvpn neutron object BGPVPN_RT = "64512:2" # Function to obtain stack parameters from system vars def get_keystone_creds(): d = {} try: d['username'] = os.environ['OS_USERNAME'] d['password'] = os.environ['OS_PASSWORD'] d['auth_url'] = os.environ['OS_AUTH_URL'] d['tenant_name'] = os.environ['OS_TENANT_NAME'] except KeyError: print("ERROR: Stack environment variables type " "OS_* are not properly set") sys.exit(1) return d # Main function def main(): # Call function that imports (dev)stack vars creds = get_keystone_creds() # Neutron object # It dynamically loads the BGPVPN API neutron = client.Client(**creds) try: # Network object creation. This dummy network will be used to bind the # attached subnet to the BGPVPN object. # Creation of the Network net_obj = neutron.create_network({'network': {'name': "dummyNet"}}) # Verify creation print('Network created\t[network-id:%s]...' % net_obj['network']['id']) # Creation of the subnet, is attached to the created network subnet_obj = neutron.create_subnet( {'subnet': {'name': "dummySubnet", 'cidr': SUBNET_IP, 'network_id': net_obj['network']['id'], 'ip_version': 4}}) # Verify print("Subnet created\t[subnet-id:%s]..." % subnet_obj['subnet']['id']) # Creation of a BGPVPN object. This object is created with the # required parameter 'routes_targets'. # This object can be created with others parameters or be updated with # them by calling the update function on the object. print("\nBGPVPN object handling.") # Creation of the BGPVPN object bgpvpn_obj = neutron.create_bgpvpn( {'bgpvpn': {'route_targets': [BGPVPN_RT]}}) print("BGPVPN object created\t[bgpvpn-id:%s]..." % bgpvpn_obj['bgpvpn']['id']) # Update the BGPVPN object bgpvpn_obj = neutron.update_bgpvpn( bgpvpn_obj['bgpvpn']['id'], {'bgpvpn': {'name': "dummyBGPVPN"}}) # List all BGPVPN objects list_bgpvpn_obj = neutron.list_bgpvpns() print("List of all BGPVPN object\t[%s]" % list_bgpvpn_obj) # List of all BGPVPN objects filtered on the type parameter set to l3 # value list_bgpvpn_obj = neutron.list_bgpvpns(type='l3') print("List of all BGPVPN object with type=l3\t[%s]" % list_bgpvpn_obj) # Creation of a BGPVPN Network association. print("\nBGPVPN Network Association object handling.") # Creation of a Network Association bound on the created BGPVPN object bgpvpn_net_assoc_obj = neutron.create_network_association( bgpvpn_obj['bgpvpn']['id'], {'network_association': {'network_id': net_obj['network']['id']}}) print("BGPVPN Network Association created\t" "[network_association:%s]..." % bgpvpn_net_assoc_obj['network_association']['id']) # List all NETWORK ASSOCIATION object filtered on the network created # above list_bgpvpn_net_assoc_obj = neutron.list_network_associations( bgpvpn_obj['bgpvpn']['id'], network_id=net_obj['network']['id']) print("List of NETWORK ASSOCIATION objects using network_id" "[%s]\t[%s]" % (net_obj['network']['id'], list_bgpvpn_net_assoc_obj)) # Deletion of all objects created in this example print("\nDeletion of all created objects") # First declared associations related of the created BGPVPN object in # this example neutron.delete_network_association( bgpvpn_net_assoc_obj['network_association']['id'], bgpvpn_obj['bgpvpn']['id']) # Then the BGPVPN object neutron.delete_bgpvpn(bgpvpn_obj['bgpvpn']['id']) # Subnet neutron.delete_subnet(subnet_obj['subnet']['id']) # And finally the Network neutron.delete_network(net_obj['network']['id']) except Exception as e: print("[ERROR][%s]" % str(e)) sys.exit(1) print("[Done]") if __name__ == '__main__': main() __all__ = ['main'] networking-bgpvpn-8.0.0/doc/source/samples/__init__.py0000666000175100017510000000000013245511235023037 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/devstack/0000775000175100017510000000000013245511747017041 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/devstack/gate_hook.sh0000666000175100017510000000071113245511235021326 0ustar zuulzuul00000000000000#!/bin/sh set -xe testtype=${1:-"unknown"} GATE_DEST=$BASE/new sudo modprobe bridge case "$testtype" in dsvm-bagpipe-functional*|dsvm-functional*) # Used by configure_for_func_testing DEVSTACK_PATH=$GATE_DEST/devstack IS_GATE=True source "$GATE_DEST"/neutron/tools/configure_for_func_testing.sh configure_host_for_func_testing sudo chown -R stack:stack /opt/stack ;; *) $GATE_DEST/devstack-gate/devstack-vm-gate.sh esac networking-bgpvpn-8.0.0/devstack/post_test_hook.sh0000777000175100017510000000203713245511235022440 0ustar zuulzuul00000000000000#!/usr/bin/env bash set -xe NETWORKING_BGPVPN_DIR="$BASE/new/networking-bgpvpn" LOGS=/opt/stack/logs GATE_STACK_USER=stack SCRIPTS_DIR="/usr/os-testr-env/bin/" typetest=${1:-"unknown"} function generate_testr_results { sudo -H -u $GATE_STACK_USER chmod o+rw . sudo -H -u $GATE_STACK_USER chmod o+rw -R .stestr if [ -f ".stestr/0" ] ; then .tox/$venv/bin/subunit-1to2 < .stestr/0 > ./stestr.subunit $SCRIPTS_DIR/subunit2html ./stestr.subunit testr_results.html gzip -9 ./stestr.subunit gzip -9 ./testr_results.html sudo mv *.gz $LOGS fi } case "$typetest" in dsvm-bagpipe-functional*|dsvm-functional*) cd $NETWORKING_BGPVPN_DIR sudo chown -R $GATE_STACK_USER:$GATE_STACK_USER . # Run tests venv=${typetest/dsvm-bagpipe/dsvm} echo "Running networking-bgpvpn $venv test suite" set +e sudo -H -u $GATE_STACK_USER tox -e ${venv} testr_exit_code=$? set -e # Collect and parse results generate_testr_results exit $testr_exit_code ;; *) ;; esac networking-bgpvpn-8.0.0/devstack/pre_test_hook.sh0000666000175100017510000000007213245511235022233 0ustar zuulzuul00000000000000# place holder until we have something useful to do here networking-bgpvpn-8.0.0/devstack/settings0000777000175100017510000000053013245511235020617 0ustar zuulzuul00000000000000NETWORKING_BGPVPN_DIR="$DEST/networking-bgpvpn" NETWORKING_BGPVPN_CONF="$NEUTRON_CONF_DIR/networking_bgpvpn.conf" BGPVPN_DASHBOARD_ENABLE="$NETWORKING_BGPVPN_DIR/bgpvpn_dashboard/enabled/*" NETWORKING_BGPVPN_DRIVER=${NETWORKING_BGPVPN_DRIVER:-BGPVPN:Dummy:networking_bgpvpn.neutron.services.service_drivers.driver_api.BGPVPNDriverRC:default} networking-bgpvpn-8.0.0/devstack/devstack-gate-rc0000666000175100017510000000031113245511235022075 0ustar zuulzuul00000000000000# This file is hooked from https://github.com/openstack-infra/project-config/blob/master/jenkins/jobs/networking-bgpvpn.yaml export OVERRIDE_ENABLED_SERVICES=q-svc,q-agt,q-dhcp,q-l3,key,mysql,rabbit networking-bgpvpn-8.0.0/devstack/devstack-gate-bagpipe-rc0000666000175100017510000000263313245511271023513 0ustar zuulzuul00000000000000# This file is hooked from https://github.com/openstack-infra/project-config/blob/master/jenkins/jobs/networking-bgpvpn.yaml export OVERRIDE_ENABLED_SERVICES=n-api,n-crt,n-cpu,n-cond,n-sch,placement-api,q-svc,q-agt,g-api,g-reg,q-dhcp,q-l3,q-meta,key,mysql,rabbit,b-bgp,n-api-meta if [[ $DEVSTACK_GATE_TEMPEST -eq 1 ]] ; then export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_TEMPEST_REGEX="^networking_bgpvpn_tempest\." export OVERRIDE_ENABLED_SERVICES=${OVERRIDE_ENABLED_SERVICES},tempest fi export DEVSTACK_LOCAL_CONFIG+=$'\n'"NETWORKING_BGPVPN_DRIVER=BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe_v2.BaGPipeBGPVPNDriver:default" export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin networking-bagpipe https://git.openstack.org/openstack/networking-bagpipe" export DEVSTACK_LOCAL_CONFIG+=$'\n'"BAGPIPE_DATAPLANE_DRIVER_IPVPN=ovs" # until we do multinode, there is no BGP peer to connect to export DEVSTACK_LOCAL_CONFIG+=$'\n'"BAGPIPE_BGP_PEERS=-" # https://bugs.launchpad.net/devstack/+bug/1567052 # so we need VERBOSE=False until bagpipe-bgp uses rootwrap and is not run with sudo (same for bagpipe-fakerr) export DEVSTACK_LOCAL_CONFIG+=$'\n'"VERBOSE=False" # at least some DB setup things (e.g. for functional tests) require # helpers from neutron devstack plugin export DEVSTACK_LOCAL_CONFIG+=$'\n'"enable_plugin neutron git://git.openstack.org/openstack/neutron" networking-bgpvpn-8.0.0/devstack/plugin.sh0000777000175100017510000000737613245511235020705 0ustar zuulzuul00000000000000#!/bin/bash # Save trace setting _XTRACE_NETWORKING_BGPVPN=$(set +o | grep xtrace) set -o xtrace if is_service_enabled q-agt; then source $NEUTRON_DIR/devstack/lib/l2_agent fi if [[ "$1" == "source" ]]; then # no-op : elif [[ "$1" == "stack" && "$2" == "install" ]]; then echo_summary "Installing networking-bgpvpn" setup_develop $NETWORKING_BGPVPN_DIR elif [[ "$1" == "stack" && "$2" == "pre-install" ]]; then echo_summary "Enabling networking-bgpvpn service plugin" _neutron_service_plugin_class_add bgpvpn if [[ "$Q_AGENT" == "bagpipe-openvswitch" ]]; then echo "networking-bagpipe: you don't need to set Q_AGENT=bagpipe-openvswitch anymore" Q_AGENT=openvswitch`` fi elif [[ "$1" == "stack" && "$2" == "test-config" ]]; then if is_service_enabled tempest; then echo_summary "Enabling bgpvpn in $TEMPEST_CONFIG" fi elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then if is_service_enabled q-svc; then echo_summary "Configuring networking-bgpvpn" mkdir -v -p $NEUTRON_CONF_DIR/policy.d && cp -v $NETWORKING_BGPVPN_DIR/etc/neutron/policy.d/bgpvpn.conf $NEUTRON_CONF_DIR/policy.d mkdir -v -p $(dirname $NETWORKING_BGPVPN_CONF) && cp -v $NETWORKING_BGPVPN_DIR/etc/neutron/networking_bgpvpn.conf $NETWORKING_BGPVPN_CONF inicomment $NETWORKING_BGPVPN_CONF service_providers service_provider iniadd $NETWORKING_BGPVPN_CONF service_providers service_provider $NETWORKING_BGPVPN_DRIVER neutron_server_config_add $NETWORKING_BGPVPN_CONF fi if is_service_enabled q-agt && is_service_enabled b-bgp ; then echo_summary "Configuring agent for bagpipe bgpvpn" plugin_agent_add_l2_agent_extension bagpipe_bgpvpn configure_l2_agent if [[ "$Q_AGENT" == "openvswitch" ]]; then # l2pop and arp_responder are required for bagpipe driver iniset /$Q_PLUGIN_CONF_FILE agent l2_population True iniset /$Q_PLUGIN_CONF_FILE agent arp_responder True elif [[ "$Q_AGENT" == "linuxbridge" ]]; then # l2pop is required for EVPN/VXLAN bagpipe driver iniset /$Q_PLUGIN_CONF_FILE vxlan l2_population True else die $LINENO "unsupported agent for networking-bagpipe: $Q_AGENT" fi fi if is_service_enabled h-eng; then echo_summary "Enabling bgpvpn in $HEAT_CONF" iniset $HEAT_CONF DEFAULT plugin_dirs $NETWORKING_BGPVPN_DIR/networking_bgpvpn_heat fi if is_service_enabled horizon; then cp $BGPVPN_DASHBOARD_ENABLE $HORIZON_DIR/openstack_dashboard/local/enabled/ # Add policy file for BGPVPN_DASHBOARD _set_policy_file $DEST/horizon/openstack_dashboard/local/local_settings.py \ networking-bgpvpn $NETWORKING_BGPVPN_DIR/bgpvpn_dashboard/etc/bgpvpn-horizon.conf fi fi function _ensure_policy_file { local file=$1 # Look for POLICY_FILES dict. start=$(grep -nE '^\s*POLICY_FILES\s*=\s*' $file | cut -d : -f 1) if [ ! -n "$start" ]; then # If POLICY_FILES is not found, define it. cat <> $file POLICY_FILES = { 'identity': 'keystone_policy.json', 'compute': 'nova_policy.json', 'volume': 'cinder_policy.json', 'image': 'glance_policy.json', 'orchestration': 'heat_policy.json', 'network': 'neutron_policy.json', } EOF fi } function _set_policy_file { local file=$1 local policy_name=$2 local policy_file=$3 _ensure_policy_file $file echo "POLICY_FILES['$policy_name'] = '$policy_file'" >> $file } if [[ "$1" == "unstack" ]]; then #no-op : fi if [[ "$1" == "clean" ]]; then # Remove bgpvpn-dashboard enabled files and pyc rm -f $HORIZON_DIR/openstack_dashboard/local/enabled/*_bgpvpn_panel* fi # Restore XTRACE ${_XTRACE_NETWORKING_BGPVPN} networking-bgpvpn-8.0.0/babel-djangojs.cfg0000666000175100017510000000110113245511235020543 0ustar zuulzuul00000000000000[extractors] # We use a custom extractor to find translatable strings in AngularJS # templates. The extractor is included in horizon.utils for now. # See http://babel.pocoo.org/docs/messages/#referencing-extraction-methods for # details on how this works. angular = horizon.utils.babel_extract_angular:extract_angular [javascript: **.js] # We need to look into all static folders for HTML files. # The **/static ensures that we also search within # /openstack_dashboard/dashboards/XYZ/static which will ensure # that plugins are also translated. [angular: **/static/**.html] networking-bgpvpn-8.0.0/LICENSE0000666000175100017510000002363713245511235016247 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. networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/0000775000175100017510000000000013245511747022472 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/SOURCES.txt0000664000175100017510000002752013245511747024364 0ustar zuulzuul00000000000000.coveragerc .mailmap .pylintrc .stestr.conf .testr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel-django.cfg babel-djangojs.cfg babel.cfg requirements.txt setup.cfg setup.py test-requirements.txt tox.ini bgpvpn_dashboard/__init__.py bgpvpn_dashboard/api/__init__.py bgpvpn_dashboard/api/bgpvpn.py bgpvpn_dashboard/common/__init__.py bgpvpn_dashboard/common/bgpvpn.py bgpvpn_dashboard/dashboards/__init__.py bgpvpn_dashboard/dashboards/admin/__init__.py bgpvpn_dashboard/dashboards/admin/bgpvpn/__init__.py bgpvpn_dashboard/dashboards/admin/bgpvpn/forms.py bgpvpn_dashboard/dashboards/admin/bgpvpn/panel.py bgpvpn_dashboard/dashboards/admin/bgpvpn/tables.py bgpvpn_dashboard/dashboards/admin/bgpvpn/urls.py bgpvpn_dashboard/dashboards/admin/bgpvpn/views.py bgpvpn_dashboard/dashboards/admin/bgpvpn/workflows.py bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_create.html bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_detail_overview.html bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/create.html bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/detail.html bgpvpn_dashboard/dashboards/project/__init__.py bgpvpn_dashboard/dashboards/project/bgpvpn/__init__.py bgpvpn_dashboard/dashboards/project/bgpvpn/forms.py bgpvpn_dashboard/dashboards/project/bgpvpn/panel.py bgpvpn_dashboard/dashboards/project/bgpvpn/tables.py bgpvpn_dashboard/dashboards/project/bgpvpn/urls.py bgpvpn_dashboard/dashboards/project/bgpvpn/views.py bgpvpn_dashboard/dashboards/project/bgpvpn/workflows.py bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_networks.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_routers.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_detail_overview.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_modify.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/detail.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/index.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/modify.html bgpvpn_dashboard/enabled/_1495_project_bgpvpn_panel.py bgpvpn_dashboard/enabled/_2360_admin_bgpvpn_panel.py bgpvpn_dashboard/enabled/__init__.py bgpvpn_dashboard/etc/bgpvpn-horizon.conf bgpvpn_dashboard/locale/fr/LC_MESSAGES/django.po bgpvpn_dashboard/test/__init__.py bgpvpn_dashboard/test/helpers.py bgpvpn_dashboard/test/settings.py bgpvpn_dashboard/test/test.py bgpvpn_dashboard/test/urls.py bgpvpn_dashboard/test/admin/__init__.py bgpvpn_dashboard/test/admin/test_forms.py bgpvpn_dashboard/test/admin/test_tables.py bgpvpn_dashboard/test/admin/test_views.py bgpvpn_dashboard/test/api_tests/__init__.py bgpvpn_dashboard/test/api_tests/bgpvpn_tests.py bgpvpn_dashboard/test/project/__init__.py bgpvpn_dashboard/test/project/test_forms.py bgpvpn_dashboard/test/project/test_tables.py bgpvpn_dashboard/test/project/test_views.py bgpvpn_dashboard/test/test_data/__init__.py bgpvpn_dashboard/test/test_data/bgpvpn_data.py bgpvpn_dashboard/test/test_data/utils.py devstack/devstack-gate-bagpipe-rc devstack/devstack-gate-rc devstack/gate_hook.sh devstack/plugin.sh devstack/post_test_hook.sh devstack/pre_test_hook.sh devstack/settings doc/source/conf.py doc/source/index.rst doc/source/_static/.placeholder doc/source/configuration/index.rst doc/source/configuration/networking-bgpvpn.rst doc/source/configuration/opencontrail-driver.rst doc/source/configuration/samples/networking-bgpvpn.rst doc/source/configuration/samples/opencontrail-driver.rst doc/source/contributor/contributing.rst doc/source/contributor/index.rst doc/source/contributor/specs.rst doc/source/contributor/future/attributes.rst doc/source/contributor/future/index.rst doc/source/install/index.rst doc/source/samples/__init__.py doc/source/samples/bgpvpn-sample01.py doc/source/user/api.rst doc/source/user/components-sdn.blockdiag doc/source/user/heat.rst doc/source/user/horizon.rst doc/source/user/index.rst doc/source/user/overview.rst doc/source/user/usage.rst doc/source/user/workflows.seqdiag doc/source/user/drivers/index.rst doc/source/user/drivers/bagpipe/index.rst doc/source/user/drivers/bagpipe/overview.blockdiag doc/source/user/drivers/nuage/index.rst doc/source/user/drivers/opencontrail/index.rst doc/source/user/drivers/opendaylight/index.rst etc/neutron/networking_bgpvpn.conf etc/neutron/policy.d/bgpvpn.conf etc/oslo-config-generator/networking-bgpvpn.conf etc/oslo-config-generator/opencontrail-driver.conf networking_bgpvpn/__init__.py networking_bgpvpn/_i18n.py networking_bgpvpn/version.py networking_bgpvpn.egg-info/PKG-INFO networking_bgpvpn.egg-info/SOURCES.txt networking_bgpvpn.egg-info/dependency_links.txt networking_bgpvpn.egg-info/entry_points.txt networking_bgpvpn.egg-info/not-zip-safe networking_bgpvpn.egg-info/pbr.json networking_bgpvpn.egg-info/requires.txt networking_bgpvpn.egg-info/top_level.txt networking_bgpvpn/neutron/__init__.py networking_bgpvpn/neutron/opts.py networking_bgpvpn/neutron/db/__init__.py networking_bgpvpn/neutron/db/bgpvpn_db.py networking_bgpvpn/neutron/db/head.py networking_bgpvpn/neutron/db/migration/__init__.py networking_bgpvpn/neutron/db/migration/alembic_migrations/README networking_bgpvpn/neutron/db/migration/alembic_migrations/__init__.py networking_bgpvpn/neutron/db/migration/alembic_migrations/env.py networking_bgpvpn/neutron/db/migration/alembic_migrations/script.py.mako networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEAD networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/start_networking_bgpvpn.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/contract/180baa4183e0_initial.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/17d9fd4fddee_initial.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/3600132c6147_add_router_association_table.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/contract/23ce05e0a19f_rename_tenant_to_project.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/expand/0ab4049986b8_add_indexes_to_tenant_id.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/39411aacf9b8_add_vni_to_bgpvpn_table.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/4610803bdf0d_router_assoc_add_advertise_extra_routes.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/666c706fea3b_bgpvpn_local_pref.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/9a6664f3b8d4_add_port_association_table.py networking_bgpvpn/neutron/extensions/__init__.py networking_bgpvpn/neutron/extensions/bgpvpn.py networking_bgpvpn/neutron/extensions/bgpvpn_routes_control.py networking_bgpvpn/neutron/extensions/bgpvpn_vni.py networking_bgpvpn/neutron/services/__init__.py networking_bgpvpn/neutron/services/plugin.py networking_bgpvpn/neutron/services/common/__init__.py networking_bgpvpn/neutron/services/common/constants.py networking_bgpvpn/neutron/services/common/utils.py networking_bgpvpn/neutron/services/service_drivers/__init__.py networking_bgpvpn/neutron/services/service_drivers/driver_api.py networking_bgpvpn/neutron/services/service_drivers/bagpipe/__init__.py networking_bgpvpn/neutron/services/service_drivers/bagpipe/bagpipe.py networking_bgpvpn/neutron/services/service_drivers/bagpipe/bagpipe_v2.py networking_bgpvpn/neutron/services/service_drivers/opencontrail/__init__.py networking_bgpvpn/neutron/services/service_drivers/opencontrail/exceptions.py networking_bgpvpn/neutron/services/service_drivers/opencontrail/opencontrail.py networking_bgpvpn/neutron/services/service_drivers/opencontrail/opencontrail_client.py networking_bgpvpn/neutron/services/service_drivers/opendaylight/__init__.py networking_bgpvpn/neutron/services/service_drivers/opendaylight/odl.py networking_bgpvpn/neutronclient/__init__.py networking_bgpvpn/neutronclient/neutron/__init__.py networking_bgpvpn/neutronclient/neutron/v2_0/__init__.py networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/__init__.py networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/bgpvpn.py networking_bgpvpn/tests/__init__.py networking_bgpvpn/tests/functional/__init__.py networking_bgpvpn/tests/functional/requirements.txt networking_bgpvpn/tests/functional/test_placeholder.py networking_bgpvpn/tests/functional/db/__init__.py networking_bgpvpn/tests/functional/db/test_migrations.py networking_bgpvpn/tests/unit/__init__.py networking_bgpvpn/tests/unit/client/__init__.py networking_bgpvpn/tests/unit/client/test_client.py networking_bgpvpn/tests/unit/db/__init__.py networking_bgpvpn/tests/unit/db/test_db.py networking_bgpvpn/tests/unit/extensions/__init__.py networking_bgpvpn/tests/unit/extensions/test_bgpvpn.py networking_bgpvpn/tests/unit/extensions/test_bgpvpn_rc_base.py networking_bgpvpn/tests/unit/extensions/test_bgpvpn_routes_control.py networking_bgpvpn/tests/unit/extensions/test_bgpvpn_vni.py networking_bgpvpn/tests/unit/services/__init__.py networking_bgpvpn/tests/unit/services/test_plugin.py networking_bgpvpn/tests/unit/services/bagpipe/__init__.py networking_bgpvpn/tests/unit/services/bagpipe/test_bagpipe.py networking_bgpvpn/tests/unit/services/common/__init__.py networking_bgpvpn/tests/unit/services/common/test_utils.py networking_bgpvpn/tests/unit/services/odl/__init__.py networking_bgpvpn/tests/unit/services/odl/test_odl.py networking_bgpvpn_heat/__init__.py networking_bgpvpn_heat/_i18n.py networking_bgpvpn_heat/bgpvpnservice.py networking_bgpvpn_heat/examples/bgpvpn_test-00.yaml networking_bgpvpn_heat/examples/bgpvpn_test-01-admin.yaml networking_bgpvpn_heat/examples/bgpvpn_test-01-tenant.yaml networking_bgpvpn_heat/examples/bgpvpn_test-01bis_router-tenant.yaml networking_bgpvpn_heat/examples/bgpvpn_test-04-admin.yaml networking_bgpvpn_heat/examples/bgpvpn_test-04-tenant.yaml networking_bgpvpn_heat/locale/en_GB/LC_MESSAGES/networking_bgpvpn_heat.po networking_bgpvpn_tempest/README.rst networking_bgpvpn_tempest/__init__.py networking_bgpvpn_tempest/config.py networking_bgpvpn_tempest/plugin.py networking_bgpvpn_tempest/services/__init__.py networking_bgpvpn_tempest/services/bgpvpn/__init__.py networking_bgpvpn_tempest/services/bgpvpn/bgpvpn_client.py networking_bgpvpn_tempest/tests/__init__.py networking_bgpvpn_tempest/tests/base.py networking_bgpvpn_tempest/tests/api/__init__.py networking_bgpvpn_tempest/tests/api/test_bgpvpn.py networking_bgpvpn_tempest/tests/scenario/__init__.py networking_bgpvpn_tempest/tests/scenario/manager.py networking_bgpvpn_tempest/tests/scenario/test_bgpvpn_basic.py releasenotes/notes/.placeholder releasenotes/notes/0_heat-support-ab233de7401aeb36.yaml releasenotes/notes/add-vni-to-bgpvpn-31d6eda7ba6d5047.yaml releasenotes/notes/bagpipe-driver-improvements-401a7ba59a6f5f45.yaml releasenotes/notes/bagpipe-router-compat-b53b6f3799cd23db.yaml releasenotes/notes/bagpipe_enable_evpn-ae64f77df89e069b.yaml releasenotes/notes/bgpvpn_service_declaration-6d9ecd2c397e4821.yaml releasenotes/notes/deprecate-old-opencontrail-driver-a598892ddf54c724.yaml releasenotes/notes/filtering-on-resource-association-2acdbc5b59d1a40a.yaml releasenotes/notes/horizon-support-06a7b21286002949.yaml releasenotes/notes/mitaka-prelude-1675467c144a91ea.yaml releasenotes/notes/odl_router_association-fa2ed7c396531418.yaml releasenotes/notes/pre_commit_checks-b902ee19a3654a7b.yaml releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst releasenotes/source/mitaka.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po specs/bgpvpn.rst tools/django-manage.py tools/generate_config_file_samples.sh tools/ostestr_compat_shim.sh tools/test-setup.sh tools/tox_install.shnetworking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/dependency_links.txt0000664000175100017510000000000113245511745026536 0ustar zuulzuul00000000000000 networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/entry_points.txt0000664000175100017510000000150013245511745025762 0ustar zuulzuul00000000000000[heat.constraints] neutron.bgpvpn = networking_bgpvpn_heat.bgpvpnservice:BGPVPNConstraint [neutron.db.alembic_migrations] networking-bgpvpn = networking_bgpvpn.neutron.db.migration:alembic_migrations [neutron.service_plugins] bgpvpn = networking_bgpvpn.neutron.services.plugin:BGPVPNPlugin [neutronclient.extension] bgpvpn = networking_bgpvpn.neutronclient.neutron.v2_0.bgpvpn.bgpvpn [oslo.config.opts] networking-bgpvpn.opencontrail_driver = networking_bgpvpn.neutron.opts:list_opencontrail_driver_opts networking-bgpvpn.service_provider = networking_bgpvpn.neutron.opts:list_service_provider [oslo.config.opts.defaults] networking-bgpvpn.service_provider = networking_bgpvpn.neutron.opts:set_service_provider_default [tempest.test_plugins] networking-bgpvpn = networking_bgpvpn_tempest.plugin:NetworkingBgpvpnTempestPlugin networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/not-zip-safe0000664000175100017510000000000113245511666024720 0ustar zuulzuul00000000000000 networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/requires.txt0000664000175100017510000000034613245511745025073 0ustar zuulzuul00000000000000pbr!=2.1.0,>=2.0.0 Babel!=2.4.0,>=2.3.4 oslo.config>=5.1.0 oslo.db>=4.27.0 oslo.i18n>=3.15.3 oslo.log>=3.36.0 oslo.utils>=3.33.0 sphinxcontrib-blockdiag>=1.5.4 sphinxcontrib-seqdiag>=0.8.4 neutron-lib>=1.13.0 debtcollector>=1.2.0 networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/pbr.json0000664000175100017510000000005613245511745024147 0ustar zuulzuul00000000000000{"git_version": "cc671c8", "is_release": true}networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/top_level.txt0000664000175100017510000000012413245511745025217 0ustar zuulzuul00000000000000bgpvpn_dashboard networking_bgpvpn networking_bgpvpn_heat networking_bgpvpn_tempest networking-bgpvpn-8.0.0/networking_bgpvpn.egg-info/PKG-INFO0000664000175100017510000000463213245511745023572 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: networking-bgpvpn Version: 8.0.0 Summary: API and Framework to interconnect bgpvpn to neutron networks Home-page: https://docs.openstack.org/networking-bgpvpn/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/networking-bgpvpn.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on =============================================== BGP-MPLS VPN Extension for OpenStack Networking =============================================== This project provides an API and Framework to interconnect BGP/MPLS VPNs to Openstack Neutron networks, routers and ports. The Border Gateway Protocol and Multi-Protocol Label Switching are widely used Wide Area Networking technologies. The primary purpose of this project is to allow attachment of Neutron networks and/or routers to VPNs built in carrier provided WANs using these standard protocols. An additional purpose of this project is to enable the use of these technologies within the Neutron networking environment. A vendor-neutral API and data model are provided such that multiple backends may be "plugged in" while offering the same tenant facing API. A reference implementation based on an Open Source BGP implementation is also provided. * Free software: Apache license * Source: http://git.openstack.org/cgit/openstack/networking-bgpvpn * Bugs: http://bugs.launchpad.net/bgpvpn * Doc: https://docs.openstack.org/networking-bgpvpn/latest/ 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 :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/0000775000175100017510000000000013245511747022541 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/config.py0000666000175100017510000000276213245511235024361 0ustar zuulzuul00000000000000# Copyright (c) 2015 Ericsson. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg BgpvpnGroup = [ cfg.IntOpt('min_asn', default=100, help=("Minimum number for the range of " "autonomous system number for distinguishers.")), cfg.IntOpt('min_nn', default=100, help=("Minimum number for the range of " "assigned number for distinguishers.")), cfg.IntOpt('max_asn', default=200, help=("Maximum number for the range of " "autonomous system number for distinguishers.")), cfg.IntOpt('max_nn', default=200, help=("Maximum number for the range of " "assigned number for distinguishers.")), ] bgpvpn_group = cfg.OptGroup(name="bgpvpn", title=("Networking-Bgpvpn Service " "Options")) networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/plugin.py0000666000175100017510000000251113245511235024402 0ustar zuulzuul00000000000000# Copyright (c) 2015 Ericsson. # 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 os from tempest.test_discover import plugins from networking_bgpvpn_tempest import config as project_config class NetworkingBgpvpnTempestPlugin(plugins.TempestPlugin): def load_tests(self): base_path = os.path.split(os.path.dirname( os.path.abspath(__file__)))[0] test_dir = "networking_bgpvpn_tempest/tests" full_test_dir = os.path.join(base_path, test_dir) return full_test_dir, base_path def register_opts(self, conf): conf.register_group(project_config.bgpvpn_group) conf.register_opts(project_config.BgpvpnGroup, group="bgpvpn") def get_opt_lists(self): return [(project_config.bgpvpn_group.name, project_config.BgpvpnGroup)] networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/README.rst0000666000175100017510000000212413245511235024221 0ustar zuulzuul00000000000000=============================================== Tempest Integration of networking_bgpvpn =============================================== This directory contains Tempest tests to cover the networking_bgpvpn project. If you are running the tests locally you should set the env variables: # TEMPEST_CONFIG_DIR=/opt/stack/tempest/etc # OS_TEST_PATH=/opt/stack/networking_bgpvpn_tempest/tests If the tests take too long to execute so that a test timeout failure happens, that timeout value may need to be increased. This is done by setting the environment variable OS_TEST_TIMEOUT to a sufficiently large value. Examples - three ways to run Tempest scenario tests: ==================================================== Using tempest client: --------------------- TEMPEST_CONFIG_DIR=/opt/stack/tempest/etc OS_TEST_PATH=./networking_bgpvpn_tempest/tests/ tempest run --regex scenario Using ostestr: -------------- TEMPEST_CONFIG_DIR=/opt/stack/tempest/etc OS_TEST_PATH=./networking_bgpvpn_tempest/tests/ ostestr --regex scenario Using tox: ---------- TEMPEST_CONFIG_DIR=/opt/stack/tempest/etc tox -e scenario networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/__init__.py0000666000175100017510000000000013245511235024632 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/services/0000775000175100017510000000000013245511747024364 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/services/bgpvpn/0000775000175100017510000000000013245511747025660 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/services/bgpvpn/bgpvpn_client.py0000666000175100017510000001131113245511235031053 0ustar zuulzuul00000000000000# Copyright (c) 2015 Ericsson. # 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 tempest.lib.services.network import base # This is the representation of the bgpvpn # client of networking-bgpvpn BGPVPN_OBJECT_PATH = '/bgpvpn/bgpvpns' BGPVPN_RESOURCE_PATH = '/bgpvpn/bgpvpns/%s' BGPVPN_NETWORK_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/network_associations' BGPVPN_ROUTER_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/router_associations' BGPVPN_PORT_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/port_associations' class BgpvpnClient(base.BaseNetworkClient): def create_bgpvpn(self, **kwargs): uri = BGPVPN_OBJECT_PATH post_data = {'bgpvpn': kwargs} return self.create_resource(uri, post_data) def delete_bgpvpn(self, bgpvpn_id): uri = BGPVPN_RESOURCE_PATH % bgpvpn_id return self.delete_resource(uri) def show_bgpvpn(self, bgpvpn_id, **fields): uri = BGPVPN_RESOURCE_PATH % bgpvpn_id return self.show_resource(uri, **fields) def list_bgpvpns(self, **filters): uri = BGPVPN_OBJECT_PATH return self.list_resources(uri, **filters) def update_bgpvpn(self, bgpvpn_id, **kwargs): uri = BGPVPN_RESOURCE_PATH % bgpvpn_id post_data = {'bgpvpn': kwargs} return self.update_resource(uri, post_data) def create_network_association(self, bgpvpn_id, network_id): uri = BGPVPN_NETWORK_ASSOCIATION_PATH % bgpvpn_id post_data = {"network_association": {"network_id": network_id}} return self.create_resource(uri, post_data) def delete_network_association(self, bgpvpn_id, association_id): uri_pattern = BGPVPN_NETWORK_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) return self.delete_resource(uri) def show_network_association(self, bgpvpn_id, association_id, **fields): uri_pattern = BGPVPN_NETWORK_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) return self.show_resource(uri, **fields) def list_network_associations(self, bgpvpn_id, **filters): uri = BGPVPN_NETWORK_ASSOCIATION_PATH % bgpvpn_id return self.list_resources(uri, **filters) def create_router_association(self, bgpvpn_id, router_id): uri = BGPVPN_ROUTER_ASSOCIATION_PATH % bgpvpn_id post_data = {"router_association": {"router_id": router_id}} return self.create_resource(uri, post_data) def delete_router_association(self, bgpvpn_id, association_id): uri_pattern = BGPVPN_ROUTER_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) return self.delete_resource(uri) def show_router_association(self, bgpvpn_id, association_id, **fields): uri_pattern = BGPVPN_ROUTER_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) return self.show_resource(uri, **fields) def list_router_associations(self, bgpvpn_id, **filters): uri = BGPVPN_ROUTER_ASSOCIATION_PATH % bgpvpn_id return self.list_resources(uri, **filters) def create_port_association(self, bgpvpn_id, **kwargs): uri = BGPVPN_PORT_ASSOCIATION_PATH % bgpvpn_id post_data = {"port_association": kwargs} return self.create_resource(uri, post_data) def update_port_association(self, bgpvpn_id, association_id, **kwargs): uri_pattern = BGPVPN_PORT_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) post_data = {"port_association": kwargs} return self.update_resource(uri, post_data) def delete_port_association(self, bgpvpn_id, association_id): uri_pattern = BGPVPN_PORT_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) return self.delete_resource(uri) def show_port_association(self, bgpvpn_id, association_id, **fields): uri_pattern = BGPVPN_PORT_ASSOCIATION_PATH + "/%s" uri = uri_pattern % (bgpvpn_id, association_id) return self.show_resource(uri, **fields) def list_port_associations(self, bgpvpn_id, **filters): uri = BGPVPN_PORT_ASSOCIATION_PATH % bgpvpn_id return self.list_resources(uri, **filters) networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/services/bgpvpn/__init__.py0000666000175100017510000000000013245511235027751 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/services/__init__.py0000666000175100017510000000000013245511235026455 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/0000775000175100017510000000000013245511747023703 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/api/0000775000175100017510000000000013245511747024454 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/api/test_bgpvpn.py0000666000175100017510000004160513245511235027361 0ustar zuulzuul00000000000000# Copyright (c) 2015 Ericsson. # 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 networking_bgpvpn_tempest.tests.base import BaseBgpvpnTest as base from oslo_utils import uuidutils from tempest.lib.common.utils import data_utils from tempest.lib import decorators from tempest.lib import exceptions from testtools import ExpectedException class BgpvpnTest(base): """Tests the following operations in the Neutron API: create bgpvpn delete bgpvpn show bgpvpn list bgpvpns associate network to bgpvpn disassociate network from bgpvpn show network association list network associations update route targets v2.0 of the Neutron API is assumed. It is also assumed that the following options are defined in the [network] section of etc/tempest.conf: ... """ @decorators.idempotent_id('4f90deb2-eb8e-4e7d-9d68-c5b5cc657f7e') def test_create_bgpvpn(self): self.create_bgpvpn(self.bgpvpn_admin_client) @decorators.attr(type=['negative']) @decorators.idempotent_id('0a911d61-d908-4c21-a11e-e403ac0d8e38') def test_create_bgpvpn_as_non_admin_fail(self): self.assertRaises(exceptions.Forbidden, self.create_bgpvpn, self.bgpvpn_client) @decorators.idempotent_id('709b23b0-9719-47df-9f53-b0812a5d5a48') def test_delete_bgpvpn(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) self.delete_bgpvpn(self.bgpvpn_admin_client, bgpvpn) @decorators.attr(type=['negative']) @decorators.idempotent_id('596abfc2-fd89-491d-863d-25459db1df4b') def test_delete_bgpvpn_as_non_admin_fail(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) self.assertRaises(exceptions.Forbidden, self.bgpvpn_client.delete_bgpvpn, bgpvpn['id']) @decorators.idempotent_id('9fa29db8-35d0-4beb-a986-23c369499ab1') def test_show_bgpvpn(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) bgpvpn_details = self.bgpvpn_client.show_bgpvpn(bgpvpn['id'])['bgpvpn'] self.assertEqual(bgpvpn['id'], bgpvpn_details['id']) @decorators.attr(type=['negative']) @decorators.idempotent_id('b20110bb-393b-4342-8b30-6486cd2b4fc6') def test_show_bgpvpn_as_non_owner_fail(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) self.assertRaises(exceptions.NotFound, self.bgpvpn_alt_client.show_bgpvpn, bgpvpn['id']) @decorators.idempotent_id('7a7feca2-1c24-4f5d-ad4b-b0e5a712adb1') def test_list_bgpvpn(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) bgpvpns = self.bgpvpn_client.list_bgpvpns()['bgpvpns'] self.assertIn(bgpvpn['id'], [bgpvpn_alt['id'] for bgpvpn_alt in bgpvpns]) @decorators.attr(type=['negative']) @decorators.idempotent_id('4875e65d-0b65-40c0-9efd-309420686ab4') def test_list_bgpvpn_as_non_owner_fail(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) bgpvpns_alt = self.bgpvpn_alt_client.list_bgpvpns()['bgpvpns'] self.assertNotIn(bgpvpn['id'], [bgpvpn_alt['id'] for bgpvpn_alt in bgpvpns_alt]) @decorators.idempotent_id('096281da-356d-4c04-bd55-784a26bb1b0c') def test_list_show_network_association(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) network = self.networks_client.create_network()['network'] association = self.bgpvpn_client.create_network_association( bgpvpn['id'], network['id'])['network_association'] net_assocs = self.bgpvpn_client\ .list_network_associations(bgpvpn['id'])['network_associations'] self.assertIn(association['id'], [net_assoc['id'] for net_assoc in net_assocs]) net_assoc_details = self.bgpvpn_client\ .show_network_association(bgpvpn['id'], association['id'])['network_association'] self.assertEqual(association['id'], net_assoc_details['id']) @decorators.attr(type=['negative']) @decorators.idempotent_id('57b0da93-8e37-459f-9aaf-f903acc36025') def test_show_netassoc_as_non_owner_fail(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) network = self.networks_client.create_network()['network'] net_assoc = self.bgpvpn_client.create_network_association( bgpvpn['id'], network['id'])['network_association'] self.assertRaises(exceptions.NotFound, self.bgpvpn_alt_client.show_network_association, bgpvpn['id'], net_assoc['id']) @decorators.attr(type=['negative']) @decorators.idempotent_id('2cbb10af-bf9c-4b32-b6a6-4066de783758') def test_list_netassoc_as_non_owner_fail(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) network = self.networks_client.create_network()['network'] self.bgpvpn_client.create_network_association(bgpvpn['id'], network['id']) net_assocs_alt = self.bgpvpn_alt_client\ .list_network_associations(bgpvpn['id']) self.assertFalse(net_assocs_alt['network_associations']) @decorators.idempotent_id('51e1b079-aefa-4c37-8b1a-0567b3ef7954') def test_associate_disassociate_network(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) network = self.networks_client.create_network() network_id = network['network']['id'] # Associate the network to the bgpvpn resource association = self.bgpvpn_client.create_network_association( bgpvpn['id'], network_id) self.assertEqual(association['network_association']['network_id'], network_id) updated_bgpvpn = self.bgpvpn_client.show_bgpvpn(bgpvpn['id']) self.assertEqual(updated_bgpvpn['bgpvpn']['networks'], [network_id]) # Disassociate the network from the bgpvpn resource self.bgpvpn_client.delete_network_association( bgpvpn['id'], association['network_association']['id']) updated_bgpvpn = self.bgpvpn_client.show_bgpvpn(bgpvpn['id']) self.assertEqual(updated_bgpvpn['bgpvpn']['networks'], []) self.networks_client.delete_network(network_id) @decorators.idempotent_id('559013fd-1e34-4fde-9599-f8aafe9ae716') def test_update_route_target(self): bgpvpn = self.create_bgpvpn( self.bgpvpn_admin_client, route_targets=['64512:1'], import_targets=['64512:2'], export_targets=['64512:3']) bgpvpn = self.bgpvpn_admin_client.update_bgpvpn( bgpvpn['id'], route_targets=['64512:4'], import_targets=['64512:5'], export_targets=['64512:6'] )['bgpvpn'] self.assertEqual(['64512:4'], bgpvpn['route_targets']) self.assertEqual(['64512:5'], bgpvpn['import_targets']) self.assertEqual(['64512:6'], bgpvpn['export_targets']) @decorators.attr(type=['negative']) @decorators.idempotent_id('e35eb9be-fe1f-406c-b36b-fc1879328313') def test_update_route_target_non_admin_fail(self): bgpvpn = self.create_bgpvpn( self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id, route_targets=['64512:1']) with ExpectedException(exceptions.Forbidden): self.bgpvpn_client.update_bgpvpn( bgpvpn['id'], route_targets=['64512:2'], import_targets=['64512:3'], export_targets=['64512:4']) @decorators.attr(type=['negative']) @decorators.idempotent_id('464ca6f9-86e4-4ee3-9c65-f1edae93223d') def test_create_bgpvpn_with_invalid_routetargets(self): """Create a bgpvpn with invalid route target This test verifies that invalid route targets,import targets, export targets are rejected by the Create API """ postdata = { "name": "testbgpvpn", "tenant_id": self.bgpvpn_client.tenant_id, "route_targets": ["0"] } self.assertRaises(exceptions.BadRequest, self.bgpvpn_admin_client.create_bgpvpn, **postdata) postdata = { "name": "testbgpvpn", "tenant_id": self.bgpvpn_client.tenant_id, "import_targets": ["test", " "] } self.assertRaises(exceptions.BadRequest, self.bgpvpn_admin_client.create_bgpvpn, **postdata) postdata = { "name": "testbgpvpn", "tenant_id": self.bgpvpn_client.tenant_id, "export_targets": ["64512:1000000000000", "xyz"] } self.assertRaises(exceptions.BadRequest, self.bgpvpn_admin_client.create_bgpvpn, **postdata) @decorators.attr(type=['negative']) @decorators.idempotent_id('7d4e9b87-e1ab-47a7-a8d6-9d179365556a') def test_update_bgpvpn_invalid_routetargets(self): """Update the bgpvpn with invalid route targets This test verifies that invalid route targets,import targets and export targets are rejected by the Update API """ postdata = { "name": "testbgpvpn", "tenant_id": self.bgpvpn_client.tenant_id, } bgpvpn = self.bgpvpn_admin_client.create_bgpvpn(**postdata) updatedata = { "route_targets": ["0"] } self.assertRaises(exceptions.BadRequest, self.bgpvpn_admin_client.update_bgpvpn, bgpvpn['bgpvpn']['id'], **updatedata) updatedata = { "import_targets": ["test", " "] } self.assertRaises(exceptions.BadRequest, self.bgpvpn_admin_client.update_bgpvpn, bgpvpn['bgpvpn']['id'], **updatedata) updatedata = { "export_targets": ["64512:1000000000000", "xyz"], } self.assertRaises(exceptions.BadRequest, self.bgpvpn_admin_client.update_bgpvpn, bgpvpn['bgpvpn']['id'], **updatedata) @decorators.attr(type=['negative']) @decorators.idempotent_id('f049ce21-d239-47c0-b13f-fb57a2a558ce') def test_associate_invalid_network(self): """Associate the invalid network in bgpvpn This test verifies that invalid network id,bgpvpn id are rejected by the associate API """ postdata = { "name": "testbgpvpn", "tenant_id": self.bgpvpn_client.tenant_id, } bgpvpn = self.bgpvpn_admin_client.create_bgpvpn(**postdata) network = self.networks_client.create_network() self.assertRaises(exceptions.NotFound, self.bgpvpn_client.create_network_association, bgpvpn['bgpvpn']['id'], uuidutils.generate_uuid()) self.assertRaises(exceptions.NotFound, self.bgpvpn_client.create_network_association, uuidutils.generate_uuid(), network['network']['id']) @decorators.attr(type=['negative']) @decorators.idempotent_id('078b2660-4adb-4c4c-abf0-b77bf0bface5') def test_disassociate_invalid_network(self): """Disassociate the invalid network in bgpvpn This test verifies that invalid network id, bgpvpn id are rejected by the disassociate API """ postdata = { "name": "testbgpvpn", "tenant_id": self.bgpvpn_client.tenant_id, } bgpvpn = self.bgpvpn_admin_client.create_bgpvpn(**postdata) network = self.networks_client.create_network() association = self.bgpvpn_client.create_network_association( bgpvpn['bgpvpn']['id'], network['network']['id']) self.assertEqual(association['network_association'][ 'network_id'], network['network']['id']) self.assertRaises(exceptions.NotFound, self.bgpvpn_client.delete_network_association, bgpvpn['bgpvpn']['id'], uuidutils.generate_uuid()) self.assertRaises(exceptions.NotFound, self.bgpvpn_client.delete_network_association, uuidutils.generate_uuid(), association['network_association']['id']) @decorators.idempotent_id('de8d94b0-0239-4a48-9574-c3a4a4f7cacb') def test_associate_disassociate_router(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) router = self.routers_client.create_router() router_id = router['router']['id'] # Associate the network to the bgpvpn resource association = self.bgpvpn_client.create_router_association( bgpvpn['id'], router_id) self.assertEqual(association['router_association']['router_id'], router_id) updated_bgpvpn = self.bgpvpn_client.show_bgpvpn(bgpvpn['id']) self.assertEqual(updated_bgpvpn['bgpvpn']['routers'], [router_id]) # Disassociate the network from the bgpvpn resource self.bgpvpn_client.delete_router_association( bgpvpn['id'], association['router_association']['id']) updated_bgpvpn = self.bgpvpn_client.show_bgpvpn(bgpvpn['id']) self.assertEqual(updated_bgpvpn['bgpvpn']['routers'], []) self.routers_client.delete_router(router_id) @decorators.idempotent_id('3ae91755-b1b6-4c62-a699-a44eeb4ee522') def test_list_show_router_association(self): bgpvpn = self.create_bgpvpn(self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) router = self.routers_client.create_router() router_id = router['router']['id'] association = self.bgpvpn_client.create_router_association( bgpvpn['id'], router_id)['router_association'] rtr_assocs = self.bgpvpn_client\ .list_router_associations(bgpvpn['id'])['router_associations'] self.assertIn(association['id'], [rtr_assoc['id'] for rtr_assoc in rtr_assocs]) rtr_assoc_details = self.bgpvpn_client\ .show_router_association(bgpvpn['id'], association['id'])['router_association'] self.assertEqual(association['id'], rtr_assoc_details['id']) @decorators.attr(type=['negative']) @decorators.idempotent_id('4be1f073-fe57-4858-b7b9-9a189e90b770') def test_attach_associated_subnet_to_associated_router(self): # Create a first bgpvpn and associate a network with a subnet to it bgpvpn_net = self.create_bgpvpn( self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) network = self.create_network() subnet = self.create_subnet(network) self.bgpvpn_client.create_network_association( bgpvpn_net['id'], network['id']) # Create a second bgpvpn and associate a router to it bgpvpn_router = self.create_bgpvpn( self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id) router = self.create_router( router_name=data_utils.rand_name('test-bgpvpn-')) self.bgpvpn_client.create_router_association( bgpvpn_router['id'], router['id']) # Attach the subnet of the network to the router subnet_data = {'subnet_id': subnet['id']} self.assertRaises(exceptions.Conflict, self.routers_client.add_router_interface, router['id'], **subnet_data) networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/api/__init__.py0000666000175100017510000000000013245511235026545 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/base.py0000666000175100017510000000644013245511235025165 0ustar zuulzuul00000000000000# Copyright (c) 2015 Ericsson. # 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 networking_bgpvpn_tempest.services.bgpvpn import bgpvpn_client import tempest.api.network.base as test from tempest.common import utils from tempest import config from tempest.lib.common.utils import data_utils CONF = config.CONF class BaseBgpvpnTest(test.BaseNetworkTest): """Base class for the Bgpvpn tests that use the Tempest Neutron REST client """ credentials = ['primary', 'admin', 'alt'] bgpvpn_client = None bgpvpn_admin_client = None bgpvpn_alt_client = None @classmethod def resource_cleanup(cls): for bgpvpn in cls.bgpvpns: cls.bgpvpn_admin_client.delete_bgpvpn(bgpvpn['id']) super(BaseBgpvpnTest, cls).resource_cleanup() @classmethod def resource_setup(cls): cls.route_distinguishers = [] cls.bgpvpns = [] cls.bgpvpn_client = bgpvpn_client.BgpvpnClient( cls.os_primary.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **cls.os_primary.default_params) cls.bgpvpn_admin_client = bgpvpn_client.BgpvpnClient( cls.os_admin.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **cls.os_admin.default_params) cls.bgpvpn_alt_client = bgpvpn_client.BgpvpnClient( cls.os_alt.auth_provider, CONF.network.catalog_type, CONF.network.region or CONF.identity.region, endpoint_type=CONF.network.endpoint_type, build_interval=CONF.network.build_interval, build_timeout=CONF.network.build_timeout, **cls.os_alt.default_params) super(BaseBgpvpnTest, cls).resource_setup() @classmethod def skip_checks(cls): super(BaseBgpvpnTest, cls).skip_checks() if not utils.is_extension_enabled('bgpvpn', 'network'): msg = "Bgpvpn extension not enabled." raise cls.skipException(msg) def create_bgpvpn(self, client, **kwargs): if 'name' not in kwargs: kwargs['name'] = data_utils.rand_name('test-bgpvpn-') body = client.create_bgpvpn(**kwargs) bgpvpn = body['bgpvpn'] self.bgpvpns.append(bgpvpn) return bgpvpn def delete_bgpvpn(self, client, bgpvpn): client.delete_bgpvpn(bgpvpn['id']) self.bgpvpns.remove(bgpvpn) networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/scenario/0000775000175100017510000000000013245511747025506 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/scenario/test_bgpvpn_basic.py0000666000175100017510000014663313245511271031563 0ustar zuulzuul00000000000000# Copyright 2016 Cisco Systems, Inc. # All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from tempest.common import compute from tempest.common import utils from tempest.common import waiters from tempest import config from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import decorators from networking_bgpvpn_tempest.tests import base from networking_bgpvpn_tempest.tests.scenario import manager CONF = config.CONF LOG = logging.getLogger(__name__) RT1 = '64512:1' RT2 = '64512:2' NET_A = 'A' NET_A_BIS = 'A-Bis' NET_B = 'B' NET_A_S1 = '10.101.11.0/24' NET_A_S2 = '10.101.12.0/24' NET_B_S1 = '10.102.21.0/24' NET_B_S2 = '10.102.22.0/24' IP_A_S1_1 = '10.101.11.10' IP_B_S1_1 = '10.102.21.20' IP_A_S1_2 = '10.101.11.30' IP_B_S1_2 = '10.102.21.40' IP_A_S1_3 = '10.101.11.50' IP_B_S1_3 = '10.102.21.60' IP_A_S2_1 = '10.101.12.50' IP_B_S2_1 = '10.102.22.60' IP_A_BIS_S1_1 = IP_A_S1_1 IP_A_BIS_S1_2 = IP_A_S1_2 IP_A_BIS_S1_3 = IP_A_S1_3 IP_A_BIS_S2_1 = IP_A_S2_1 class TestBGPVPNBasic(base.BaseBgpvpnTest, manager.NetworkScenarioTest): def setUp(self): super(TestBGPVPNBasic, self).setUp() self.servers_keypairs = {} self.servers = [] self.server_fixed_ips = {} self.ports = {} self.networks = {} self.subnets = {} self.server_fips = {} self._create_security_group_for_test() @decorators.idempotent_id('afdd6cad-871a-4343-b97b-6319c76c815d') @utils.services('compute', 'network') def test_bgpvpn_basic(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Associate network A and network B to a given L3 BGPVPN 5. Create router and connect it to network A 6. Give a FIP to server 1 7. Check that server 1 can ping server 2 """ self._create_networks_and_subnets() self._create_servers() self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('8a5a6fac-313c-464b-9c5c-29d4e1c0a51e') @utils.services('compute', 'network') def test_bgpvpn_variant1(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Associate network A and network B to a given L3 BGPVPN 3. Start up server 1 in network A 4. Start up server 2 in network B 5. Create router and connect it to network A 6. Give a FIP to server 1 7. Check that server 1 can ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self._create_servers() self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('e7468636-0816-4092-82ca-3590680ed00b') @utils.services('compute', 'network') def test_bgpvpn_variant2(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Create router and associate to network B 5. Associate network A and network B to a given L3 BGPVPN 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 """ self._create_networks_and_subnets() self._create_servers() self.router_b = self._create_fip_router( subnet_id=self.subnets[NET_B][0]['id']) self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('7c66aa31-fb3a-4e15-8808-46eb361f153a') @utils.services('compute', 'network') def test_bgpvpn_variant3(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Create router and connect it to network B 5. Associate network A and network B to a given L3 BGPVPN 6. Delete router associated to network B 7. Create router and connect it to network A 8. Give a FIP to server 1 9. Check that server 1 can ping server 2 """ self._create_networks_and_subnets() self._create_servers() self.router_b = self._create_fip_router( subnet_id=self.subnets[NET_B][0]['id']) self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self.delete_router(self.router_b) self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('973ab26d-c7d8-4a32-9aa9-2d7e6f406135') @utils.services('compute', 'network') def test_bgpvpn_variant4(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Associate network A and network B to a given L3 BGPVPN 5. Create router and connect it to network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 2 can ping server 1 """ self._create_networks_and_subnets() self._create_servers() self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self.router_b = self._create_fip_router( subnet_id=self.subnets[NET_B][0]['id']) self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('2ac0696b-e828-4299-9e94-5f9c4988d961') @utils.services('compute', 'network') def test_bgpvpn_variant5(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Create router and connect it to network B 3. Associate network A and network B to a given L3 BGPVPN 4. Start up server 1 in network A 5. Start up server 2 in network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 2 can ping server 1 """ self._create_networks_and_subnets() self.router_b = self._create_fip_router( subnet_id=self.subnets[NET_B][0]['id']) self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self._create_servers() self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('9081338e-a52e-46bb-a40e-bda24ec4b1bd') @utils.services('compute', 'network') def test_bgpvpn_variant6(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Associate network A and network B to a given L3 BGPVPN 3. Create router and connect it to network B 4. Start up server 1 in network A 5. Start up server 2 in network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 2 can ping server 1 """ self._create_networks_and_subnets() self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self.router_b = self._create_fip_router( subnet_id=self.subnets[NET_B][0]['id']) self._create_servers() self._associate_fip_and_check_l3_bgpvpn() @decorators.idempotent_id('133497a1-2788-40f7-be01-b3b64b5ef8cd') @utils.services('compute', 'network') def test_bgpvpn_update_route_targets_disjoint_targets(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Create L3 BGPVPN with only RT defined 5. Associate network A to a given L3 BGPVPN 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 9. Update L3 BGPVPN to have eRT<>iRT and no RT what is insufficient for proper connectivity between network A and B 10. Check that server 1 cannot ping server 2 11. Update L3 BGPVPN to have again only RT defined 12. Check that server 1 can ping again server 2 """ self._create_networks_and_subnets() self._create_servers() self._create_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._associate_all_nets_to_bgpvpn() self._associate_fip_and_check_l3_bgpvpn() self._update_l3_bgpvpn(rts=[], import_rts=[RT1], export_rts=[RT2]) self._check_l3_bgpvpn(should_succeed=False) self._update_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._check_l3_bgpvpn() @decorators.idempotent_id('bf417cad-0bc4-446a-b367-850aa619ca4f') @utils.services('compute', 'network') def test_bgpvpn_update_route_targets_common_target(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Create L3 BGPVPN with only RT defined 5. Associate network A to a given L3 BGPVPN 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 9. Update L3 BGPVPN to have eRT<>iRT and RT=iRT 10. Check that server 1 can ping server 2 11. Update L3 BGPVPN to have again only RT defined 12. Check that server 1 can ping again server 2 """ self._create_networks_and_subnets() self._create_servers() self._create_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._associate_all_nets_to_bgpvpn() self._associate_fip_and_check_l3_bgpvpn() self._update_l3_bgpvpn(rts=[RT1], import_rts=[RT1], export_rts=[RT2]) self._check_l3_bgpvpn() self._update_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._check_l3_bgpvpn() @decorators.idempotent_id('08d4f40e-3cec-485b-9da2-76e67fbd9881') @utils.services('compute', 'network') def test_bgpvpn_update_route_targets_and_unassociated_net(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Create invalid L3 BGPVPN with eRT<>iRT that is insufficient for proper connectivity between network A and B 5. Associate network A to a given L3 BGPVPN 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 cannot ping server 2 9. Associate network B to a given L3 BGPVPN 10. Check that server 1 cannot ping server 2 11. Update L3 BGPVPN to have only RT defined 12. Check that server 1 can ping server 2 """ self._create_networks_and_subnets() self._create_servers() self.router = self._create_router_and_associate_fip( 0, self.subnets[NET_A][0]) self._create_l3_bgpvpn(rts=[], export_rts=[RT1], import_rts=[RT2]) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) self._check_l3_bgpvpn(should_succeed=False) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_B]['id']) self._check_l3_bgpvpn(should_succeed=False) self._update_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._check_l3_bgpvpn() @decorators.idempotent_id('c8bfd695-f731-47a6-86e3-3dfa492e08e0') @utils.services('compute', 'network') def test_bgpvpn_update_rt_and_keep_local_connectivity_variant1(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Start up server 3 in network A 5. Start up server 4 in network B 6. Create invalid L3 BGPVPN with eRT<>iRT that is insufficient for proper connectivity between network A and B 7. Associate network A to a given L3 BGPVPN 8. Create router A and connect it to network A 9. Give a FIP to server 1 10. Check that server 1 cannot ping server 2 11. Check that server 1 can ping server 3 12. Associate network B to a given L3 BGPVPN 13. Create router B and connect it to network B 14. Give a FIP to server 2 15. Check that server 1 still cannot ping server 2 16. Check that server 2 can ping server 4 17. Update L3 BGPVPN to have now only RT defined 18. Check that server 1 can now ping server 2 19. Check that server 1 still can ping server 3 20. Check that server 2 still can ping server 4 """ self._create_networks_and_subnets() self._create_l3_bgpvpn(rts=[], import_rts=[RT1], export_rts=[RT2]) self._create_servers([[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_B], IP_B_S1_1], [self.networks[NET_A], IP_A_S1_2], [self.networks[NET_B], IP_B_S1_2]]) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) self.router_a = self._create_router_and_associate_fip( 0, self.subnets[NET_A][0]) self._check_l3_bgpvpn(should_succeed=False) self._check_l3_bgpvpn(self.servers[0], self.servers[2]) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_B]['id']) self.router_b = self._create_router_and_associate_fip( 1, self.subnets[NET_B][0]) self._check_l3_bgpvpn(should_succeed=False) self._check_l3_bgpvpn(self.servers[1], self.servers[3]) self._update_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._check_l3_bgpvpn() self._check_l3_bgpvpn(self.servers[0], self.servers[2]) self._check_l3_bgpvpn(self.servers[1], self.servers[3]) @decorators.idempotent_id('758a8731-5070-4b1e-9a66-d6ff05bb5be1') @utils.services('compute', 'network') def test_bgpvpn_update_rt_and_keep_local_connectivity_variant2(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Start up server 1 in network A 3. Start up server 2 in network B 4. Start up server 3 in network A 5. Start up server 4 in network B 6. Create invalid L3 BGPVPN with eRT<>iRT that is insufficient for proper connectivity between network A and B 7. Create router A and connect it to network A 8. Give a FIP to server 1 9. Create router B and connect it to network B 10. Give a FIP to server 4 11. Associate network A to a given L3 BGPVPN 12. Check that server 1 cannot ping server 2 13. Check that server 1 can ping server 3 14. Associate router B to a given L3 BGPVPN 15. Check that server 1 still cannot ping server 2 16. Check that server 4 can ping server 2 17. Update L3 BGPVPN to have now only RT defined 18. Check that server 1 can now ping server 2 19. Check that server 1 still can ping server 3 20. Check that server 4 still can ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn(rts=[], import_rts=[RT1], export_rts=[RT2]) self._create_servers([[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_B], IP_B_S1_1], [self.networks[NET_A], IP_A_S1_2], [self.networks[NET_B], IP_B_S1_2]]) self._create_router_and_associate_fip( 0, self.subnets[NET_A][0]) router_b = self._create_router_and_associate_fip( 3, self.subnets[NET_B][0]) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) self._check_l3_bgpvpn(should_succeed=False) self._check_l3_bgpvpn(self.servers[0], self.servers[2]) self.bgpvpn_client.create_router_association(self.bgpvpn['id'], router_b['id']) self._check_l3_bgpvpn(should_succeed=False) self._check_l3_bgpvpn(self.servers[3], self.servers[1]) self._update_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._check_l3_bgpvpn() self._check_l3_bgpvpn(self.servers[0], self.servers[2]) self._check_l3_bgpvpn(self.servers[3], self.servers[1]) @decorators.idempotent_id('876b49bc-f34a-451b-ba3c-d74295838130') @utils.services('compute', 'network') @utils.requires_ext(extension='bgpvpn-routes-control', service='network') def test_bgpvpn_port_association_local_pref(self): """This test checks port association in BGPVPN. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN 3. Start up server 1 in network A 4. Start up server 2 in network B 5. Start up server 3 in network B 6. Create router and connect it to network A 7. Create router and connect it to network B 8. Give a FIP to all servers 9. Setup dummy HTTP service on server 2 and 3 10. Configure ip forwarding on server 2 11. Configure ip forwarding on server 3 12. Configure alternative loopback address on server 2 13. Configure alternative loopback address on server 3 14. Associate network A to a given L3 BGPVPN 15. Associate port of server 2 to a given L3 BGPVPN with higher local_pref value 16. Associate port of server 3 to a given L3 BGPVPN with lower local_pref value 17. Check that server 1 pings server's 2 alternative ip 18. Update port association of server 2 to have now lower local_pref value 19. Update port association of server 3 to have now higher local_pref value 20. Check that server 1 pings now server's 3 alternative ip """ self._create_networks_and_subnets(port_security=False) self._create_l3_bgpvpn() self._create_servers([[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_B], IP_B_S1_1], [self.networks[NET_B], IP_B_S1_2]], port_security=False) self._create_fip_router(subnet_id=self.subnets[NET_A][0]['id']) self._create_fip_router(subnet_id=self.subnets[NET_B][0]['id']) self._associate_fip(0) self._associate_fip(1) self._associate_fip(2) self._setup_http_server(1) self._setup_http_server(2) self._setup_ip_forwarding(1) self._setup_ip_forwarding(2) alternative_loopback_ip = '192.168.0.1' alternative_loopback_cidr = '192.168.0.0/24' self._setup_ip_address(1, alternative_loopback_cidr) self._setup_ip_address(2, alternative_loopback_cidr) primary_port_routes = [{'type': 'prefix', 'local_pref': 200, 'prefix': alternative_loopback_cidr}] alternate_port_routes = [{'type': 'prefix', 'local_pref': 100, 'prefix': alternative_loopback_cidr}] self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) port_id_1 = self.ports[self.servers[1]['id']]['id'] body = self.bgpvpn_client.create_port_association( self.bgpvpn['id'], port_id=port_id_1, routes=primary_port_routes) port_association_1 = body['port_association'] port_id_2 = self.ports[self.servers[2]['id']]['id'] body = self.bgpvpn_client.create_port_association( self.bgpvpn['id'], port_id=port_id_2, routes=alternate_port_routes) port_association_2 = body['port_association'] destination_srv_1 = '%s:%s' % (self.servers[1]['name'], self.servers[1]['id']) destination_srv_2 = '%s:%s' % (self.servers[2]['name'], self.servers[2]['id']) self._check_l3_bgpvpn_by_specific_ip( to_server_ip=alternative_loopback_ip, validate_server=destination_srv_1) self.bgpvpn_client.update_port_association( self.bgpvpn['id'], port_association_1['id'], routes=alternate_port_routes) self.bgpvpn_client.update_port_association( self.bgpvpn['id'], port_association_2['id'], routes=primary_port_routes) self._check_l3_bgpvpn_by_specific_ip( to_server_ip=alternative_loopback_ip, validate_server=destination_srv_2) @decorators.idempotent_id('f762e6ac-920e-4d0f-aa67-02bdd4ab8433') @utils.services('compute', 'network') def test_bgpvpn_tenant_separation_and_local_connectivity(self): """This test checks tenant separation for BGPVPN. 1. Create networks A with subnet S1 and S2 2. Create networks A-Bis with subnet S1 and S2 (like for network A) 3. Create L3 BGPVPN for network A with RT1 4. Create L3 BGPVPN for network A-Bis with RT2 5. Associate network A to a given L3 BGPVPN 6. Associate network A-Bis to a given L3 BGPVPN 7. Start up server 1 in network A and subnet S1 8. Start up server 2 in network A-Bis and subnet S1 9. Start up server 3 in network A and subnet S1 10. Start up server 4 in network A-Bis and subnet S1 11. Start up server 5 in network A and subnet S1 12. Create router A and connect it to network A 13. Create router A-Bis and connect it to network A-Bis 14. Give a FIP to all servers 15. Setup dummy HTTP service on server 2 and 3 16. Check that server 1 pings server 3 instead of server 2 17. Check that server 1 can ping server 3 18. Check that server 2 cannot ping server 1 19. Check that server 2 pings itself instead of server 3 20. Check that server 2 can ping server 4 21. Check that server 2 pings server 4 instead of server 5 """ self._create_networks_and_subnets([NET_A, NET_A_BIS], [[NET_A_S1, NET_A_S2], [NET_A_S1, NET_A_S2]]) bgpvpn_a = self._create_l3_bgpvpn(name='test-l3-bgpvpn-a', rts=[RT1]) bgpvpn_a_bis = self._create_l3_bgpvpn(name='test-l3-bgpvpn-a-bis', rts=[RT2]) self.bgpvpn_client.create_network_association( bgpvpn_a['id'], self.networks[NET_A]['id']) self.bgpvpn_client.create_network_association( bgpvpn_a_bis['id'], self.networks[NET_A_BIS]['id']) self._create_servers([[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_A_BIS], IP_A_BIS_S1_2], [self.networks[NET_A], IP_A_S1_2], [self.networks[NET_A_BIS], IP_A_BIS_S1_3], [self.networks[NET_A], IP_A_S1_3]]) self._create_fip_router(subnet_id=self.subnets[NET_A][0]['id']) self._create_fip_router(subnet_id=self.subnets[NET_A_BIS][0]['id']) self._associate_fip(0) self._associate_fip(1) self._associate_fip(2) self._associate_fip(3) self._associate_fip(4) self._setup_http_server(1) self._setup_http_server(2) self._setup_http_server(3) self._setup_http_server(4) self._check_l3_bgpvpn(self.servers[0], self.servers[1], should_succeed=False, validate_server=True) self._check_l3_bgpvpn(self.servers[0], self.servers[2], validate_server=True) self._check_l3_bgpvpn(self.servers[1], self.servers[0], should_succeed=False) self._check_l3_bgpvpn(self.servers[1], self.servers[2], should_succeed=False, validate_server=True) self._check_l3_bgpvpn(self.servers[1], self.servers[3], validate_server=True) self._check_l3_bgpvpn(self.servers[1], self.servers[4], should_succeed=False, validate_server=True) @decorators.idempotent_id('3b44b2f4-f514-4004-8623-2682bc46bb07') @utils.services('compute', 'network') @utils.requires_ext(extension='bgpvpn-routes-control', service='network') def test_bgpvpn_port_association_create_and_update(self): """This test checks port association in BGPVPN. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN 3. Create router and connect it to network A 4. Create router and connect it to network B 5. Start up server 1 in network A 6. Start up server 2 in network B 7. Give a FIP to all servers 8. Configure ip forwarding on server 2 9. Configure alternative loopback address on server 2 10. Associate network A to a given L3 BGPVPN 11. Associate port of server 2 to a given L3 BGPVPN 12. Check that server 1 can ping server's 2 alternative ip 13. Update created before port association by routes removal 14. Check that server 1 cannot ping server's 2 alternative ip """ alternative_loopback_ip = '192.168.0.1' alternative_loopback_cidr = '192.168.0.0/24' self._create_networks_and_subnets(port_security=False) self._create_l3_bgpvpn() self._create_servers([[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_B], IP_B_S1_1]], port_security=False) self._create_fip_router(subnet_id=self.subnets[NET_A][0]['id']) self._create_fip_router(subnet_id=self.subnets[NET_B][0]['id']) self._associate_fip(0) self._associate_fip(1) # preliminary check that no connectivity to 192.168.0.1 initially # exists self._check_l3_bgpvpn_by_specific_ip( should_succeed=False, to_server_ip=alternative_loopback_ip) self._setup_ip_forwarding(1) self._setup_ip_address(1, alternative_loopback_cidr) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) port_id = self.ports[self.servers[1]['id']]['id'] port_routes = [{'type': 'prefix', 'prefix': alternative_loopback_cidr}] body = self.bgpvpn_client.create_port_association(self.bgpvpn['id'], port_id=port_id, routes=port_routes) port_association = body['port_association'] self._check_l3_bgpvpn_by_specific_ip( to_server_ip=alternative_loopback_ip) self.bgpvpn_client.update_port_association( self.bgpvpn['id'], port_association['id'], routes=[]) self._check_l3_bgpvpn_by_specific_ip( should_succeed=False, to_server_ip=alternative_loopback_ip) @decorators.idempotent_id('9c3280b5-0b32-4562-800c-0b50d9d52bfd') @utils.services('compute', 'network') @utils.requires_ext(extension='bgpvpn-routes-control', service='network') def test_bgpvpn_port_association_create_and_delete(self): """This test checks port association in BGPVPN. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN 3. Create router and connect it to network A 4. Create router and connect it to network B 5. Start up server 1 in network A 6. Start up server 2 in network B 7. Give a FIP to all servers 8. Configure ip forwarding on server 2 9. Configure alternative loopback address on server 2 10. Associate network A to a given L3 BGPVPN 11. Associate port of server 2 to a given L3 BGPVPN 12. Check that server 1 can ping server's 2 alternative ip 13. Remove created before port association 14. Check that server 1 cannot ping server's 2 alternative ip """ alternative_loopback_ip = '192.168.0.1' alternative_loopback_cidr = '192.168.0.0/24' self._create_networks_and_subnets(port_security=False) self._create_l3_bgpvpn() self._create_servers([[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_B], IP_B_S1_1]], port_security=False) self._create_fip_router(subnet_id=self.subnets[NET_A][0]['id']) self._create_fip_router(subnet_id=self.subnets[NET_B][0]['id']) self._associate_fip(0) self._associate_fip(1) # preliminary check that no connectivity to 192.168.0.1 initially # exists self._check_l3_bgpvpn_by_specific_ip( should_succeed=False, to_server_ip=alternative_loopback_ip) self._setup_ip_forwarding(1) self._setup_ip_address(1, alternative_loopback_cidr) self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) port_id = self.ports[self.servers[1]['id']]['id'] port_routes = [{'type': 'prefix', 'prefix': alternative_loopback_cidr}] body = self.bgpvpn_client.create_port_association(self.bgpvpn['id'], port_id=port_id, routes=port_routes) port_association = body['port_association'] self._check_l3_bgpvpn_by_specific_ip( to_server_ip=alternative_loopback_ip) self.bgpvpn_client.delete_port_association( self.bgpvpn['id'], port_association['id']) self._check_l3_bgpvpn_by_specific_ip( should_succeed=False, to_server_ip=alternative_loopback_ip) @decorators.idempotent_id('8478074e-22df-4234-b02b-61257b475b18') @utils.services('compute', 'network') def test_bgpvpn_negative_ping_to_unassociated_net(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN 3. Associate network A to a given L3 BGPVPN 4. Start up server 1 in network A 5. Start up server 2 in network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn() self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) self._create_servers() self._associate_fip_and_check_l3_bgpvpn(should_succeed=False) @decorators.idempotent_id('b6d219b2-90bb-431f-a566-bf6a780d1578') @utils.services('compute', 'network') def test_bgpvpn_negative_disjoint_import_export(self): """This test checks basic BGPVPN. 1. Create networks A and B with their respective subnets 2. Create invalid L3 BGPVPN with eRT<>iRT that is insufficient for proper connectivity between network A and B 3. Associate network A and B to a given L3 BGPVPN 4. Start up server 1 in network A 5. Start up server 2 in network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn(rts=[], import_rts=[RT1], export_rts=[RT2]) self._associate_all_nets_to_bgpvpn() self._create_servers() self._associate_fip_and_check_l3_bgpvpn(should_succeed=False) @decorators.idempotent_id('dc92643f-1b2c-4a3e-bc5e-ea780d721ef7') @utils.services('compute', 'network') def test_bgpvpn_negative_delete_bgpvpn(self): """This test checks BGPVPN delete. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN 3. Associate network A and network B to a given L3 BGPVPN 4. Start up server 1 in network A 5. Start up server 2 in network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 9. Delete L3 BGPVPN 10. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn() self._associate_all_nets_to_bgpvpn() self._create_servers() self._associate_fip_and_check_l3_bgpvpn() self.delete_bgpvpn(self.bgpvpn_admin_client, self.bgpvpn) self._check_l3_bgpvpn(should_succeed=False) @decorators.idempotent_id('2e6bf893-1410-4ef6-9948-1877f3a8f3d1') @utils.services('compute', 'network') def test_bgpvpn_negative_delete_net_association(self): """This test checks BGPVPN net association delete. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN 3. Associate network A and network B to a given L3 BGPVPN 4. Start up server 1 in network A 5. Start up server 2 in network B 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 9. Delete association of network A 10. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn() body = self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) assoc_b = body['network_association'] self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_B]['id']) self._create_servers() self._associate_fip_and_check_l3_bgpvpn() self.bgpvpn_admin_client.delete_network_association(self.bgpvpn['id'], assoc_b['id']) self._check_l3_bgpvpn(should_succeed=False) @decorators.idempotent_id('9ebf4342-4448-4d63-98f9-44d3a606b6cf') @utils.services('compute', 'network') def test_bgpvpn_negative_delete_router_association(self): """This test checks BGPVPN router association delete. 1. Create networks A and B with their respective subnets 2. Create router and connect it to network B 3. Create L3 BGPVPN 4. Associate network A to a given L3 BGPVPN 5. Associate router B to a given L3 BGPVPN 6. Start up server 1 in network A 7. Start up server 2 in network B 8. Create router and connect it to network A 9. Give a FIP to server 1 10. Check that server 1 can ping server 2 11. Delete association of router B 12. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() router_b = self._create_fip_router( subnet_id=self.subnets[NET_B][0]['id']) self._create_l3_bgpvpn() self.bgpvpn_client.create_network_association( self.bgpvpn['id'], self.networks[NET_A]['id']) body = self.bgpvpn_client.create_router_association(self.bgpvpn['id'], router_b['id']) assoc_b = body['router_association'] self._create_servers() self._associate_fip_and_check_l3_bgpvpn() self.bgpvpn_admin_client.delete_router_association(self.bgpvpn['id'], assoc_b['id']) self._check_l3_bgpvpn(should_succeed=False) @decorators.idempotent_id('be4471b3-5f57-4022-b719-b45a673a728b') @utils.services('compute', 'network') def test_bgpvpn_negative_update_to_disjoint_import_export(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN with only RT defined 3. Start up server 1 in network A 4. Start up server 2 in network B 5. Associate network A to a given L3 BGPVPN 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 9. Update L3 BGPVPN to have eRT<>iRT and no RT what is insufficient for proper connectivity between network A and B 10. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._create_servers() self._associate_all_nets_to_bgpvpn() self._associate_fip_and_check_l3_bgpvpn() self._update_l3_bgpvpn(rts=[], import_rts=[RT1], export_rts=[RT2]) self._check_l3_bgpvpn(should_succeed=False) @decorators.idempotent_id('fb37a546-7263-4ffe-a42c-77eca377ff1a') @utils.services('compute', 'network') def test_bgpvpn_negative_update_to_empty_rt_list(self): """This test checks basic BGPVPN route targets update. 1. Create networks A and B with their respective subnets 2. Create L3 BGPVPN with only RT defined 3. Start up server 1 in network A 4. Start up server 2 in network B 5. Associate network A and B to a given L3 BGPVPN 6. Create router and connect it to network A 7. Give a FIP to server 1 8. Check that server 1 can ping server 2 9. Update L3 BGPVPN to empty RT list what is insufficient for proper connectivity between network A and B 10. Check that server 1 cannot ping server 2 """ self._create_networks_and_subnets() self._create_l3_bgpvpn(rts=[RT1], import_rts=[], export_rts=[]) self._create_servers() self._associate_all_nets_to_bgpvpn() self._associate_fip_and_check_l3_bgpvpn() self._update_l3_bgpvpn(rts=[], import_rts=[], export_rts=[]) self._check_l3_bgpvpn(should_succeed=False) def _create_security_group_for_test(self): self.security_group = self._create_security_group( tenant_id=self.bgpvpn_client.tenant_id) def _create_networks_and_subnets(self, names=None, subnet_cidrs=None, port_security=True): if not names: names = [NET_A, NET_B] if not subnet_cidrs: subnet_cidrs = [[NET_A_S1], [NET_B_S1]] for (name, subnet_cidrs) in zip(names, subnet_cidrs): network = self._create_network( namestart=name, port_security_enabled=port_security) self.networks[name] = network self.subnets[name] = [] for (j, cidr) in enumerate(subnet_cidrs): sub_name = "subnet-%s-%d" % (name, j + 1) subnet = self._create_subnet_with_cidr(network, namestart=sub_name, cidr=cidr, ip_version=4) self.subnets[name].append(subnet) def _create_subnet_with_cidr(self, network, subnets_client=None, namestart='subnet-smoke', **kwargs): if not subnets_client: subnets_client = self.subnets_client tenant_cidr = kwargs.get('cidr') subnet = dict( name=data_utils.rand_name(namestart), network_id=network['id'], tenant_id=network['tenant_id'], **kwargs) result = subnets_client.create_subnet(**subnet) self.assertIsNotNone(result, 'Unable to allocate tenant network') subnet = result['subnet'] self.assertEqual(subnet['cidr'], tenant_cidr) self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnets_client.delete_subnet, subnet['id']) return subnet def _create_fip_router(self, client=None, public_network_id=None, subnet_id=None): router = self._create_router(client, namestart='router-') router_id = router['id'] if public_network_id is None: public_network_id = CONF.network.public_network_id if client is None: client = self.routers_client kwargs = {'external_gateway_info': {'network_id': public_network_id}} router = client.update_router(router_id, **kwargs)['router'] if subnet_id is not None: client.add_router_interface(router_id, subnet_id=subnet_id) self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.remove_router_interface, router_id, subnet_id=subnet_id) return router def _associate_fip(self, server_index): server = self.servers[server_index] fip = self.create_floating_ip( server, external_network_id=CONF.network.public_network_id, port_id=self.ports[server['id']]['id']) self.server_fips[server['id']] = fip return fip def _create_router_and_associate_fip(self, server_index, subnet): router = self._create_fip_router(subnet_id=subnet['id']) self._associate_fip(server_index) return router def _create_server(self, name, keypair, network, ip_address, security_group_ids, clients, port_security): security_groups = [] if port_security: security_groups = security_group_ids create_port_body = {'fixed_ips': [{'ip_address': ip_address}], 'namestart': 'port-smoke', 'security_groups': security_groups} port = self._create_port(network_id=network['id'], client=clients.ports_client, **create_port_body) create_server_kwargs = { 'key_name': keypair['name'], 'networks': [{'uuid': network['id'], 'port': port['id']}] } body, servers = compute.create_test_server( clients, wait_until='ACTIVE', name=name, **create_server_kwargs) self.addCleanup(waiters.wait_for_server_termination, clients.servers_client, body['id']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, clients.servers_client.delete_server, body['id']) server = clients.servers_client.show_server(body['id'])['server'] LOG.debug('Created server: %s with status: %s', server['id'], server['status']) self.ports[server['id']] = port return server def _create_servers(self, ports_config=None, port_security=True): keypair = self.create_keypair() security_group_ids = [self.security_group['id']] if ports_config is None: ports_config = [[self.networks[NET_A], IP_A_S1_1], [self.networks[NET_B], IP_B_S1_1]] for (i, port_config) in enumerate(ports_config): network = port_config[0] server = self._create_server( 'server-' + str(i + 1), keypair, network, port_config[1], security_group_ids, self.os_primary, port_security) self.servers.append(server) self.servers_keypairs[server['id']] = keypair self.server_fixed_ips[server['id']] = ( server['addresses'][network['name']][0]['addr']) self.assertTrue(self.servers_keypairs) def _create_l3_bgpvpn(self, name='test-l3-bgpvpn', rts=None, import_rts=None, export_rts=None): if rts is None: rts = [RT1] import_rts = import_rts or [] export_rts = export_rts or [] self.bgpvpn = self.create_bgpvpn( self.bgpvpn_admin_client, tenant_id=self.bgpvpn_client.tenant_id, name=name, route_targets=rts, export_targets=export_rts, import_targets=import_rts) return self.bgpvpn def _update_l3_bgpvpn(self, rts=None, import_rts=None, export_rts=None, bgpvpn=None): bgpvpn = bgpvpn or self.bgpvpn if rts is None: rts = [RT1] import_rts = import_rts or [] export_rts = export_rts or [] LOG.debug('Updating targets in BGPVPN %s', bgpvpn['id']) self.bgpvpn_admin_client.update_bgpvpn(bgpvpn['id'], route_targets=rts, export_targets=export_rts, import_targets=import_rts) def _associate_all_nets_to_bgpvpn(self, bgpvpn=None): bgpvpn = bgpvpn or self.bgpvpn for network in self.networks.values(): self.bgpvpn_client.create_network_association( bgpvpn['id'], network['id']) LOG.debug('BGPVPN network associations completed') def _setup_ssh_client(self, server): server_fip = self.server_fips[server['id']][ 'floating_ip_address'] private_key = self.servers_keypairs[server['id']][ 'private_key'] ssh_client = self.get_remote_client(server_fip, private_key=private_key) return ssh_client def _setup_http_server(self, server_index): server = self.servers[server_index] ssh_client = self._setup_ssh_client(server) ssh_client.exec_command("sudo nc -kl -p 80 -e echo '%s:%s' &" % (server['name'], server['id'])) def _setup_ip_forwarding(self, server_index): server = self.servers[server_index] ssh_client = self._setup_ssh_client(server) ssh_client.exec_command("sudo sysctl -w net.ipv4.ip_forward=1") def _setup_ip_address(self, server_index, cidr, device=None): if device is None: device = 'lo' server = self.servers[server_index] ssh_client = self._setup_ssh_client(server) ssh_client.exec_command(("sudo ip addr add {cidr} " "dev {dev}").format(cidr=cidr, dev=device)) def _log_gw_arp_info(self, ssh_client, gateway_ip): output = ssh_client.exec_command('arp -n %s' % gateway_ip) LOG.warning("Ping origin server GW ARP info:") LOG.warning(output) def _check_l3_bgpvpn(self, from_server=None, to_server=None, should_succeed=True, validate_server=False): to_server = to_server or self.servers[1] destination_srv = None if validate_server: destination_srv = '%s:%s' % (to_server['name'], to_server['id']) destination_ip = self.server_fixed_ips[to_server['id']] self._check_l3_bgpvpn_by_specific_ip(from_server=from_server, to_server_ip=destination_ip, should_succeed=should_succeed, validate_server=destination_srv) def _check_l3_bgpvpn_by_specific_ip(self, from_server=None, to_server_ip=None, should_succeed=True, validate_server=None, repeat_validate_server=10): from_server = from_server or self.servers[0] from_server_ip = self.server_fips[from_server['id']][ 'floating_ip_address'] if to_server_ip is None: to_server_ip = self.server_fixed_ips[self.servers[1]['id']] ssh_client = self._setup_ssh_client(from_server) check_reachable = should_succeed or validate_server msg = "" if check_reachable: msg = "Timed out waiting for {ip} to become reachable".format( ip=to_server_ip) else: msg = ("Unexpected ping response from VM with IP address " "{dest} originated from VM with IP address " "{src}").format(dest=to_server_ip, src=from_server_ip) try: result = self._check_remote_connectivity(ssh_client, to_server_ip, check_reachable) if check_reachable and not result: allocation = self.ports[from_server['id']]['fixed_ips'][0] subnet_id = allocation['subnet_id'] gateway_ip = '' for net_name in self.subnets: for subnet in self.subnets[net_name]: if subnet_id == subnet['id']: gateway_ip = subnet['gateway_ip'] break self._log_gw_arp_info(ssh_client, gateway_ip) ssh_client.exec_command('sudo arp -d %s' % gateway_ip) LOG.warning("Ping origin server GW ARP cleared") ssh_client.ping_host(gateway_ip) self._log_gw_arp_info(ssh_client, gateway_ip) result = self._check_remote_connectivity(ssh_client, to_server_ip, check_reachable) if not check_reachable and not result: try: content = ssh_client.exec_command( "nc %s 80" % to_server_ip).strip() LOG.warning("Can connect to %s: %s", to_server_ip, content) except Exception: LOG.warning("Could ping %s, but no http", to_server_ip) self.assertTrue(result, msg) if validate_server and result: # repeating multiple times gives increased ods of avoiding # false positives in the case where the dataplane does # equal-cost multipath for i in range(0, repeat_validate_server): real_dest = ssh_client.exec_command( "nc %s 80" % to_server_ip).strip() result = real_dest == validate_server self.assertTrue( should_succeed == result, ("Destination server name is '%s', expected is '%s'" % (real_dest, validate_server))) LOG.info("nc server name check %d successful", i) except Exception: LOG.exception( "Error validating connectivity to %s " "from VM with IP address %s: %s" % (to_server_ip, from_server_ip, msg)) raise def _associate_fip_and_check_l3_bgpvpn(self, should_succeed=True): subnet = self.subnets[NET_A][0] self.router = self._create_router_and_associate_fip(0, subnet) self._check_l3_bgpvpn(should_succeed=should_succeed) networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/scenario/manager.py0000666000175100017510000011131413245511271027465 0ustar zuulzuul00000000000000# Copyright 2012 OpenStack Foundation # Copyright 2013 IBM Corp. # 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 subprocess import netaddr from oslo_log import log from oslo_utils import netutils from tempest.common import compute from tempest.common import utils from tempest.common.utils.linux import remote_client from tempest.common.utils import net_utils from tempest.common import waiters from tempest import config from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from tempest.lib import exceptions as lib_exc import tempest.test CONF = config.CONF LOG = log.getLogger(__name__) class ScenarioTest(tempest.test.BaseTestCase): """Base class for scenario tests. Uses tempest own clients. """ credentials = ['primary'] @classmethod def setup_clients(cls): super(ScenarioTest, cls).setup_clients() # Clients (in alphabetical order) cls.keypairs_client = cls.os_primary.keypairs_client cls.servers_client = cls.os_primary.servers_client # Neutron network client cls.networks_client = cls.os_primary.networks_client cls.ports_client = cls.os_primary.ports_client cls.routers_client = cls.os_primary.routers_client cls.subnets_client = cls.os_primary.subnets_client cls.floating_ips_client = cls.os_primary.floating_ips_client cls.security_groups_client = cls.os_primary.security_groups_client cls.security_group_rules_client = ( cls.os_primary.security_group_rules_client) # ## Test functions library # # The create_[resource] functions only return body and discard the # resp part which is not used in scenario tests def _create_port(self, network_id, client=None, namestart='port-quotatest', **kwargs): if not client: client = self.ports_client name = data_utils.rand_name(namestart) result = client.create_port( name=name, network_id=network_id, **kwargs) self.assertIsNotNone(result, 'Unable to allocate port') port = result['port'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_port, port['id']) return port def create_keypair(self, client=None): if not client: client = self.keypairs_client name = data_utils.rand_name(self.__class__.__name__) # We don't need to create a keypair by pubkey in scenario body = client.create_keypair(name=name) self.addCleanup(client.delete_keypair, name) return body['keypair'] def create_server(self, name=None, image_id=None, flavor=None, validatable=False, wait_until='ACTIVE', clients=None, **kwargs): """Wrapper utility that returns a test server. This wrapper utility calls the common create test server and returns a test server. The purpose of this wrapper is to minimize the impact on the code of the tests already using this function. """ # NOTE(jlanoux): As a first step, ssh checks in the scenario # tests need to be run regardless of the run_validation and # validatable parameters and thus until the ssh validation job # becomes voting in CI. The test resources management and IP # association are taken care of in the scenario tests. # Therefore, the validatable parameter is set to false in all # those tests. In this way create_server just return a standard # server and the scenario tests always perform ssh checks. # Needed for the cross_tenant_traffic test: if clients is None: clients = self.os_primary if name is None: name = data_utils.rand_name(self.__class__.__name__ + "-server") vnic_type = CONF.network.port_vnic_type # If vnic_type is configured create port for # every network if vnic_type: ports = [] create_port_body = {'binding:vnic_type': vnic_type, 'namestart': 'port-smoke'} if kwargs: # Convert security group names to security group ids # to pass to create_port if 'security_groups' in kwargs: security_groups = \ clients.security_groups_client.list_security_groups( ).get('security_groups') sec_dict = dict([(s['name'], s['id']) for s in security_groups]) sec_groups_names = [s['name'] for s in kwargs.pop( 'security_groups')] security_groups_ids = [sec_dict[s] for s in sec_groups_names] if security_groups_ids: create_port_body[ 'security_groups'] = security_groups_ids networks = kwargs.pop('networks', []) else: networks = [] # If there are no networks passed to us we look up # for the project's private networks and create a port. # The same behaviour as we would expect when passing # the call to the clients with no networks if not networks: networks = clients.networks_client.list_networks( **{'router:external': False, 'fields': 'id'})['networks'] # It's net['uuid'] if networks come from kwargs # and net['id'] if they come from # clients.networks_client.list_networks for net in networks: net_id = net.get('uuid', net.get('id')) if 'port' not in net: port = self._create_port(network_id=net_id, client=clients.ports_client, **create_port_body) ports.append({'port': port['id']}) else: ports.append({'port': net['port']}) if ports: kwargs['networks'] = ports self.ports = ports tenant_network = self.get_tenant_network() body, servers = compute.create_test_server( clients, tenant_network=tenant_network, wait_until=wait_until, name=name, flavor=flavor, image_id=image_id, **kwargs) self.addCleanup(waiters.wait_for_server_termination, clients.servers_client, body['id']) self.addCleanup(test_utils.call_and_ignore_notfound_exc, clients.servers_client.delete_server, body['id']) server = clients.servers_client.show_server(body['id'])['server'] return server def get_remote_client(self, ip_address, username=None, private_key=None): """Get a SSH client to a remote server @param ip_address the server floating or fixed IP address to use for ssh validation @param username name of the Linux account on the remote server @param private_key the SSH private key to use @return a RemoteClient object """ if username is None: username = CONF.validation.image_ssh_user # Set this with 'keypair' or others to log in with keypair or # username/password. if CONF.validation.auth_method == 'keypair': password = None if private_key is None: private_key = self.keypair['private_key'] else: password = CONF.validation.image_ssh_password private_key = None linux_client = remote_client.RemoteClient(ip_address, username, pkey=private_key, password=password) try: linux_client.validate_authentication() except Exception as e: message = ('Initializing SSH connection to %(ip)s failed. ' 'Error: %(error)s' % {'ip': ip_address, 'error': e}) caller = test_utils.find_test_caller() if caller: message = '(%s) %s' % (caller, message) LOG.exception(message) self._log_console_output() raise return linux_client def _log_console_output(self, servers=None): if not CONF.compute_feature_enabled.console_output: LOG.debug('Console output not supported, cannot log') return if not servers: servers = self.servers_client.list_servers() servers = servers['servers'] for server in servers: try: console_output = self.servers_client.get_console_output( server['id'])['output'] LOG.debug('Console output for %s\nbody=\n%s', server['id'], console_output) except lib_exc.NotFound: LOG.debug("Server %s disappeared(deleted) while looking " "for the console log", server['id']) def _log_net_info(self, exc): # network debug is called as part of ssh init if not isinstance(exc, lib_exc.SSHTimeout): LOG.debug('Network information on a devstack host') def ping_ip_address(self, ip_address, should_succeed=True, ping_timeout=None, mtu=None): timeout = ping_timeout or CONF.validation.ping_timeout cmd = ['ping', '-c1', '-w1'] if mtu: cmd += [ # don't fragment '-M', 'do', # ping receives just the size of ICMP payload '-s', str(net_utils.get_ping_payload_size(mtu, 4)) ] cmd.append(ip_address) def ping(): proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) proc.communicate() return (proc.returncode == 0) == should_succeed caller = test_utils.find_test_caller() LOG.debug('%(caller)s begins to ping %(ip)s in %(timeout)s sec and the' ' expected result is %(should_succeed)s', { 'caller': caller, 'ip': ip_address, 'timeout': timeout, 'should_succeed': 'reachable' if should_succeed else 'unreachable' }) result = test_utils.call_until_true(ping, timeout, 1) LOG.debug('%(caller)s finishes ping %(ip)s in %(timeout)s sec and the ' 'ping result is %(result)s', { 'caller': caller, 'ip': ip_address, 'timeout': timeout, 'result': 'expected' if result else 'unexpected' }) return result def check_vm_connectivity(self, ip_address, username=None, private_key=None, should_connect=True, mtu=None): """Check server connectivity :param ip_address: server to test against :param username: server's ssh username :param private_key: server's ssh private key to be used :param should_connect: True/False indicates positive/negative test positive - attempt ping and ssh negative - attempt ping and fail if succeed :param mtu: network MTU to use for connectivity validation :raises: AssertError if the result of the connectivity check does not match the value of the should_connect param """ if should_connect: msg = "Timed out waiting for %s to become reachable" % ip_address else: msg = "ip address %s is reachable" % ip_address self.assertTrue(self.ping_ip_address(ip_address, should_succeed=should_connect, mtu=mtu), msg=msg) if should_connect: # no need to check ssh for negative connectivity self.get_remote_client(ip_address, username, private_key) def check_public_network_connectivity(self, ip_address, username, private_key, should_connect=True, msg=None, servers=None, mtu=None): # The target login is assumed to have been configured for # key-based authentication by cloud-init. LOG.debug('checking network connections to IP %s with user: %s', ip_address, username) try: self.check_vm_connectivity(ip_address, username, private_key, should_connect=should_connect, mtu=mtu) except Exception: ex_msg = 'Public network connectivity check failed' if msg: ex_msg += ": " + msg LOG.exception(ex_msg) self._log_console_output(servers) raise class NetworkScenarioTest(ScenarioTest): """Base class for network scenario tests. This class provide helpers for network scenario tests, using the neutron API. Helpers from ancestor which use the nova network API are overridden with the neutron API. This Class also enforces using Neutron instead of novanetwork. Subclassed tests will be skipped if Neutron is not enabled """ credentials = ['primary', 'admin'] @classmethod def skip_checks(cls): super(NetworkScenarioTest, cls).skip_checks() if not CONF.service_available.neutron: raise cls.skipException('Neutron not available') if not utils.is_extension_enabled('bgpvpn', 'network'): msg = "Bgpvpn extension not enabled." raise cls.skipException(msg) def _create_network(self, networks_client=None, tenant_id=None, namestart='network-smoke-', port_security_enabled=True): if not networks_client: networks_client = self.networks_client if not tenant_id: tenant_id = networks_client.tenant_id name = data_utils.rand_name(namestart) network_kwargs = dict(name=name, tenant_id=tenant_id) # Neutron disables port security by default so we have to check the # config before trying to create the network with port_security_enabled if CONF.network_feature_enabled.port_security: network_kwargs['port_security_enabled'] = port_security_enabled result = networks_client.create_network(**network_kwargs) network = result['network'] self.assertEqual(network['name'], name) self.addCleanup(test_utils.call_and_ignore_notfound_exc, networks_client.delete_network, network['id']) return network def _create_subnet(self, network, subnets_client=None, routers_client=None, namestart='subnet-smoke', **kwargs): """Create a subnet for the given network within the cidr block configured for tenant networks. """ if not subnets_client: subnets_client = self.subnets_client if not routers_client: routers_client = self.routers_client def cidr_in_use(cidr, tenant_id): """Check cidr existence :returns: True if subnet with cidr already exist in tenant False else """ cidr_in_use = self.os_admin.subnets_client.list_subnets( tenant_id=tenant_id, cidr=cidr)['subnets'] return len(cidr_in_use) != 0 ip_version = kwargs.pop('ip_version', 4) if ip_version == 6: tenant_cidr = netaddr.IPNetwork( CONF.network.project_network_v6_cidr) num_bits = CONF.network.project_network_v6_mask_bits else: tenant_cidr = netaddr.IPNetwork(CONF.network.project_network_cidr) num_bits = CONF.network.project_network_mask_bits result = None str_cidr = None # Repeatedly attempt subnet creation with sequential cidr # blocks until an unallocated block is found. for subnet_cidr in tenant_cidr.subnet(num_bits): str_cidr = str(subnet_cidr) if cidr_in_use(str_cidr, tenant_id=network['tenant_id']): continue subnet = dict( name=data_utils.rand_name(namestart), network_id=network['id'], tenant_id=network['tenant_id'], cidr=str_cidr, ip_version=ip_version, **kwargs ) try: result = subnets_client.create_subnet(**subnet) break except lib_exc.Conflict as e: is_overlapping_cidr = 'overlaps with another subnet' in str(e) if not is_overlapping_cidr: raise self.assertIsNotNone(result, 'Unable to allocate tenant network') subnet = result['subnet'] self.assertEqual(subnet['cidr'], str_cidr) self.addCleanup(test_utils.call_and_ignore_notfound_exc, subnets_client.delete_subnet, subnet['id']) return subnet def _get_server_port_id_and_ip4(self, server, ip_addr=None): ports = self.os_admin.ports_client.list_ports( device_id=server['id'], fixed_ip=ip_addr)['ports'] # A port can have more than one IP address in some cases. # If the network is dual-stack (IPv4 + IPv6), this port is associated # with 2 subnets p_status = ['ACTIVE'] # NOTE(vsaienko) With Ironic, instances live on separate hardware # servers. Neutron does not bind ports for Ironic instances, as a # result the port remains in the DOWN state. # TODO(vsaienko) remove once bug: #1599836 is resolved. if getattr(CONF.service_available, 'ironic', False): p_status.append('DOWN') port_map = [(p["id"], fxip["ip_address"]) for p in ports for fxip in p["fixed_ips"] if netutils.is_valid_ipv4(fxip["ip_address"]) and p['status'] in p_status] inactive = [p for p in ports if p['status'] != 'ACTIVE'] if inactive: LOG.warning("Instance has ports that are not ACTIVE: %s", inactive) self.assertNotEqual(0, len(port_map), "No IPv4 addresses found in: %s" % ports) self.assertEqual(len(port_map), 1, "Found multiple IPv4 addresses: %s. " "Unable to determine which port to target." % port_map) return port_map[0] def _get_network_by_name(self, network_name): net = self.os_admin.networks_client.list_networks( name=network_name)['networks'] self.assertNotEqual(len(net), 0, "Unable to get network by name: %s" % network_name) return net[0] def create_floating_ip(self, thing, external_network_id=None, port_id=None, client=None): """Create a floating IP and associates to a resource/port on Neutron""" if not external_network_id: external_network_id = CONF.network.public_network_id if not client: client = self.floating_ips_client if not port_id: port_id, ip4 = self._get_server_port_id_and_ip4(thing) else: ip4 = None result = client.create_floatingip( floating_network_id=external_network_id, port_id=port_id, tenant_id=thing['tenant_id'], fixed_ip_address=ip4 ) floating_ip = result['floatingip'] self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_floatingip, floating_ip['id']) return floating_ip def _associate_floating_ip(self, floating_ip, server): port_id, _ = self._get_server_port_id_and_ip4(server) kwargs = dict(port_id=port_id) floating_ip = self.floating_ips_client.update_floatingip( floating_ip['id'], **kwargs)['floatingip'] self.assertEqual(port_id, floating_ip['port_id']) return floating_ip def _disassociate_floating_ip(self, floating_ip): """:param floating_ip: floating_ips_client.create_floatingip""" kwargs = dict(port_id=None) floating_ip = self.floating_ips_client.update_floatingip( floating_ip['id'], **kwargs)['floatingip'] self.assertIsNone(floating_ip['port_id']) return floating_ip def check_floating_ip_status(self, floating_ip, status): """Verifies floatingip reaches the given status :param dict floating_ip: floating IP dict to check status :param status: target status :raises: AssertionError if status doesn't match """ floatingip_id = floating_ip['id'] def refresh(): result = (self.floating_ips_client. show_floatingip(floatingip_id)['floatingip']) return status == result['status'] test_utils.call_until_true(refresh, CONF.network.build_timeout, CONF.network.build_interval) floating_ip = self.floating_ips_client.show_floatingip( floatingip_id)['floatingip'] self.assertEqual(status, floating_ip['status'], message="FloatingIP: {fp} is at status: {cst}. " "failed to reach status: {st}" .format(fp=floating_ip, cst=floating_ip['status'], st=status)) LOG.info("FloatingIP: {fp} is at status: {st}" .format(fp=floating_ip, st=status)) def _check_tenant_network_connectivity(self, server, username, private_key, should_connect=True, servers_for_debug=None): if not CONF.network.project_networks_reachable: msg = 'Tenant networks not configured to be reachable.' LOG.info(msg) return # The target login is assumed to have been configured for # key-based authentication by cloud-init. try: for net_name, ip_addresses in server['addresses'].items(): for ip_address in ip_addresses: self.check_vm_connectivity(ip_address['addr'], username, private_key, should_connect=should_connect) except Exception as e: LOG.exception('Tenant network connectivity check failed') self._log_console_output(servers_for_debug) self._log_net_info(e) raise def _check_remote_connectivity(self, source, dest, should_succeed=True, nic=None, loop_for_duration=None): """check ping server via source ssh connection :param source: RemoteClient: an ssh connection from which to ping :param dest: and IP to ping against :param should_succeed: boolean should ping succeed or not :param nic: specific network interface to ping from :returns: boolean -- should_succeed == ping :returns: ping is false if ping failed """ def ping_remote(): try: source.ping_host(dest, nic=nic, count=5) except lib_exc.SSHExecCommandFailed: LOG.warning('Failed to ping IP: %s via a ssh connection ' 'from: %s.', dest, source.ssh_client.host) return not should_succeed return should_succeed loop_for_duration = loop_for_duration or CONF.validation.ping_timeout # sleep for 70s, because 60s is the default expiry time # for ARP entry ; a stale ARP entry to the gateway being # sometimes what causes the pings to fail return test_utils.call_until_true(ping_remote, loop_for_duration, 70) def _create_security_group(self, security_group_rules_client=None, tenant_id=None, namestart='secgroup-smoke', security_groups_client=None): if security_group_rules_client is None: security_group_rules_client = self.security_group_rules_client if security_groups_client is None: security_groups_client = self.security_groups_client if tenant_id is None: tenant_id = security_groups_client.tenant_id secgroup = self._create_empty_security_group( namestart=namestart, client=security_groups_client, tenant_id=tenant_id) # Add rules to the security group rules = self._create_loginable_secgroup_rule( security_group_rules_client=security_group_rules_client, secgroup=secgroup, security_groups_client=security_groups_client) for rule in rules: self.assertEqual(tenant_id, rule['tenant_id']) self.assertEqual(secgroup['id'], rule['security_group_id']) return secgroup def _create_empty_security_group(self, client=None, tenant_id=None, namestart='secgroup-smoke'): """Create a security group without rules. Default rules will be created: - IPv4 egress to any - IPv6 egress to any :param tenant_id: secgroup will be created in this tenant :returns: the created security group """ if client is None: client = self.security_groups_client if not tenant_id: tenant_id = client.tenant_id sg_name = data_utils.rand_name(namestart) sg_desc = sg_name + " description" sg_dict = dict(name=sg_name, description=sg_desc) sg_dict['tenant_id'] = tenant_id result = client.create_security_group(**sg_dict) secgroup = result['security_group'] self.assertEqual(secgroup['name'], sg_name) self.assertEqual(tenant_id, secgroup['tenant_id']) self.assertEqual(secgroup['description'], sg_desc) self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_security_group, secgroup['id']) return secgroup def _default_security_group(self, client=None, tenant_id=None): """Get default secgroup for given tenant_id. :returns: default secgroup for given tenant """ if client is None: client = self.security_groups_client if not tenant_id: tenant_id = client.tenant_id sgs = [ sg for sg in list(client.list_security_groups().values())[0] if sg['tenant_id'] == tenant_id and sg['name'] == 'default' ] msg = "No default security group for tenant %s." % (tenant_id) self.assertGreater(len(sgs), 0, msg) return sgs[0] def _create_security_group_rule(self, secgroup=None, sec_group_rules_client=None, tenant_id=None, security_groups_client=None, **kwargs): """Create a rule from a dictionary of rule parameters. Create a rule in a secgroup. if secgroup not defined will search for default secgroup in tenant_id. :param secgroup: the security group. :param tenant_id: if secgroup not passed -- the tenant in which to search for default secgroup :param kwargs: a dictionary containing rule parameters: for example, to allow incoming ssh: rule = { direction: 'ingress' protocol:'tcp', port_range_min: 22, port_range_max: 22 } """ if sec_group_rules_client is None: sec_group_rules_client = self.security_group_rules_client if security_groups_client is None: security_groups_client = self.security_groups_client if not tenant_id: tenant_id = security_groups_client.tenant_id if secgroup is None: secgroup = self._default_security_group( client=security_groups_client, tenant_id=tenant_id) ruleset = dict(security_group_id=secgroup['id'], tenant_id=secgroup['tenant_id']) ruleset.update(kwargs) sg_rule = sec_group_rules_client.create_security_group_rule(**ruleset) sg_rule = sg_rule['security_group_rule'] self.assertEqual(secgroup['tenant_id'], sg_rule['tenant_id']) self.assertEqual(secgroup['id'], sg_rule['security_group_id']) return sg_rule def _create_loginable_secgroup_rule(self, security_group_rules_client=None, secgroup=None, security_groups_client=None): """Create loginable security group rule This function will create: 1. egress and ingress tcp port 22 allow rule in order to allow ssh access for ipv4. 2. egress and ingress tcp port 80 allow rule in order to allow http access for ipv4. 3. egress and ingress ipv6 icmp allow rule, in order to allow icmpv6. 4. egress and ingress ipv4 icmp allow rule, in order to allow icmpv4. """ if security_group_rules_client is None: security_group_rules_client = self.security_group_rules_client if security_groups_client is None: security_groups_client = self.security_groups_client rules = [] rulesets = [ dict( # ssh protocol='tcp', port_range_min=22, port_range_max=22, ), dict( # http protocol='tcp', port_range_min=80, port_range_max=80, ), dict( # ping protocol='icmp', ), dict( # ipv6-icmp for ping6 protocol='icmp', ethertype='IPv6', ) ] sec_group_rules_client = security_group_rules_client for ruleset in rulesets: for r_direction in ['ingress', 'egress']: ruleset['direction'] = r_direction try: sg_rule = self._create_security_group_rule( sec_group_rules_client=sec_group_rules_client, secgroup=secgroup, security_groups_client=security_groups_client, **ruleset) except lib_exc.Conflict as ex: # if rule already exist - skip rule and continue msg = 'Security group rule already exists' if msg not in ex._error_string: raise ex else: self.assertEqual(r_direction, sg_rule['direction']) rules.append(sg_rule) return rules def _get_router(self, client=None, tenant_id=None): """Retrieve a router for the given tenant id. If a public router has been configured, it will be returned. If a public router has not been configured, but a public network has, a tenant router will be created and returned that routes traffic to the public network. """ if not client: client = self.routers_client if not tenant_id: tenant_id = client.tenant_id router_id = CONF.network.public_router_id network_id = CONF.network.public_network_id if router_id: body = client.show_router(router_id) return body['router'] elif network_id: router = self._create_router(client, tenant_id) kwargs = {'external_gateway_info': dict(network_id=network_id)} router = client.update_router(router['id'], **kwargs)['router'] return router else: raise Exception("Neither of 'public_router_id' or " "'public_network_id' has been defined.") def _create_router(self, client=None, tenant_id=None, namestart='router-smoke'): if not client: client = self.routers_client if not tenant_id: tenant_id = client.tenant_id name = data_utils.rand_name(namestart) result = client.create_router(name=name, admin_state_up=True, tenant_id=tenant_id) router = result['router'] self.assertEqual(router['name'], name) self.addCleanup(test_utils.call_and_ignore_notfound_exc, client.delete_router, router['id']) return router def _update_router_admin_state(self, router, admin_state_up): kwargs = dict(admin_state_up=admin_state_up) router = self.routers_client.update_router( router['id'], **kwargs)['router'] self.assertEqual(admin_state_up, router['admin_state_up']) def create_networks(self, networks_client=None, routers_client=None, subnets_client=None, tenant_id=None, dns_nameservers=None, port_security_enabled=True): """Create a network with a subnet connected to a router. The baremetal driver is a special case since all nodes are on the same shared network. :param tenant_id: id of tenant to create resources in. :param dns_nameservers: list of dns servers to send to subnet. :returns: network, subnet, router """ if CONF.network.shared_physical_network: # NOTE(Shrews): This exception is for environments where tenant # credential isolation is available, but network separation is # not (the current baremetal case). Likely can be removed when # test account mgmt is reworked: # https://blueprints.launchpad.net/tempest/+spec/test-accounts if not CONF.compute.fixed_network_name: m = 'fixed_network_name must be specified in config' raise lib_exc.InvalidConfiguration(m) network = self._get_network_by_name( CONF.compute.fixed_network_name) router = None subnet = None else: network = self._create_network( networks_client=networks_client, tenant_id=tenant_id, port_security_enabled=port_security_enabled) router = self._get_router(client=routers_client, tenant_id=tenant_id) subnet_kwargs = dict(network=network, subnets_client=subnets_client, routers_client=routers_client) # use explicit check because empty list is a valid option if dns_nameservers is not None: subnet_kwargs['dns_nameservers'] = dns_nameservers subnet = self._create_subnet(**subnet_kwargs) if not routers_client: routers_client = self.routers_client router_id = router['id'] routers_client.add_router_interface(router_id, subnet_id=subnet['id']) # save a cleanup job to remove this association between # router and subnet self.addCleanup(test_utils.call_and_ignore_notfound_exc, routers_client.remove_router_interface, router_id, subnet_id=subnet['id']) return network, subnet, router networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/scenario/__init__.py0000666000175100017510000000000013245511235027577 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_tempest/tests/__init__.py0000666000175100017510000000000013245511235025774 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/HACKING.rst0000666000175100017510000000024713245511235017030 0ustar zuulzuul00000000000000networking-bgpvpn Style Commandments =============================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ networking-bgpvpn-8.0.0/bgpvpn_dashboard/0000775000175100017510000000000013245511747020540 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/enabled/0000775000175100017510000000000013245511747022132 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/enabled/_2360_admin_bgpvpn_panel.py0000777000175100017510000000065013245511235027136 0ustar zuulzuul00000000000000# The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'BGPVPN Interconnections' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'admin' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'network' # Python panel class of the PANEL to be added. ADD_PANEL = ('bgpvpn_dashboard.' 'dashboards.admin.bgpvpn.panel.BGPVPNInterconnections') networking-bgpvpn-8.0.0/bgpvpn_dashboard/enabled/__init__.py0000777000175100017510000000000013245511235024226 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/enabled/_1495_project_bgpvpn_panel.py0000777000175100017510000000065413245511235027530 0ustar zuulzuul00000000000000# The slug of the panel to be added to HORIZON_CONFIG. Required. PANEL = 'BGPVPN Interconnections' # The slug of the dashboard the PANEL associated with. Required. PANEL_DASHBOARD = 'project' # The slug of the panel group the PANEL is associated with. PANEL_GROUP = 'network' # Python panel class of the PANEL to be added. ADD_PANEL = ('bgpvpn_dashboard.' 'dashboards.project.bgpvpn.panel.BGPVPNInterconnections') networking-bgpvpn-8.0.0/bgpvpn_dashboard/api/0000775000175100017510000000000013245511747021311 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/api/__init__.py0000777000175100017510000000000013245511235023405 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/api/bgpvpn.py0000777000175100017510000001024413245511235023155 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from openstack_dashboard.api import neutron LOG = logging.getLogger(__name__) neutronclient = neutron.neutronclient class Bgpvpn(neutron.NeutronAPIDictWrapper): """Wrapper for neutron bgpvpn.""" class NetworkAssociation(neutron.NeutronAPIDictWrapper): """Wrapper for neutron bgpvpn networks associations.""" class RouterAssociation(neutron.NeutronAPIDictWrapper): """Wrapper for neutron bgpvpn routers associations.""" def bgpvpns_list(request, **kwargs): LOG.debug("bgpvpn_list(): params=%s", kwargs) bgpvpns = neutronclient(request).list_bgpvpns(**kwargs).get('bgpvpns') return [Bgpvpn(v) for v in bgpvpns] def bgpvpn_get(request, bgpvpn_id, **kwargs): LOG.debug("bgpvpn_get(): bgpvpnid=%s, kwargs=%s", bgpvpn_id, kwargs) bgpvpn = neutronclient(request).show_bgpvpn(bgpvpn_id, **kwargs).get('bgpvpn') return Bgpvpn(bgpvpn) def bgpvpn_create(request, **kwargs): LOG.debug("bgpvpn_create(): params=%s", kwargs) body = {'bgpvpn': kwargs} bgpvpn = neutronclient(request).create_bgpvpn(body=body) return Bgpvpn(bgpvpn) def bgpvpn_update(request, bgpvpn_id, **kwargs): LOG.debug("bgpvpn_update(): bgpvpnid=%s, kwargs=%s", bgpvpn_id, kwargs) body = {'bgpvpn': kwargs} bgpvpn = neutronclient(request).update_bgpvpn(bgpvpn_id, body=body).get('bgpvpn') return Bgpvpn(bgpvpn) def bgpvpn_delete(request, bgpvpn_id): LOG.debug("bgpvpn_delete(): bgpvpnid=%s", bgpvpn_id) neutronclient(request).delete_bgpvpn(bgpvpn_id) def network_association_list(request, bgpvpn_id, **kwargs): LOG.debug("network_association_list(): bgpvpn_id=%s, kwargs=%s", bgpvpn_id, kwargs) network_associations = neutronclient( request).list_network_associations( bgpvpn_id, **kwargs).get('network_associations') return [NetworkAssociation(v) for v in network_associations] def network_association_create(request, bgpvpn_id, **kwargs): LOG.debug("network_association_create(): bgpvpnid=%s kwargs=%s", bgpvpn_id, kwargs) body = {'network_association': kwargs} network_association = neutronclient( request).create_network_association(bgpvpn_id, body=body) return NetworkAssociation(network_association) def network_association_delete(request, resource_id, bgpvpn_id): LOG.debug("network_association_delete(): resource_id=%s bgpvpnid=%s", resource_id, bgpvpn_id) neutronclient(request).delete_network_association(resource_id, bgpvpn_id) def router_association_list(request, bgpvpn_id, **kwargs): LOG.debug("router_association_list(): bgpvpn_id=%s, kwargs=%s", bgpvpn_id, kwargs) router_associations = neutronclient( request).list_router_associations(bgpvpn_id, **kwargs).get('router_associations') return [RouterAssociation(v) for v in router_associations] def router_association_create(request, bgpvpn_id, **kwargs): LOG.debug("router_association_create(): bgpvpnid=%s params=%s", bgpvpn_id, kwargs) body = {'router_association': kwargs} router_associations = neutronclient( request).create_router_association(bgpvpn_id, body=body) return RouterAssociation(router_associations) def router_association_delete(request, resource_id, bgpvpn_id): LOG.debug("router_association_delete(): resource_id=%s bgpvpnid=%s", resource_id, bgpvpn_id) neutronclient(request).delete_router_association(resource_id, bgpvpn_id) networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/0000775000175100017510000000000013245511747022652 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/0000775000175100017510000000000013245511747024320 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/0000775000175100017510000000000013245511747025614 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/views.py0000777000175100017510000001215613245511235027325 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.core.urlresolvers import reverse from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import tables from horizon.utils import memoized from horizon import views from horizon import workflows from openstack_dashboard import api import bgpvpn_dashboard.api.bgpvpn as bgpvpn_api from bgpvpn_dashboard.common import bgpvpn as bgpvpn_common from bgpvpn_dashboard.dashboards.project.bgpvpn import forms as bgpvpn_forms from bgpvpn_dashboard.dashboards.project.bgpvpn import tables as bgpvpn_tables from bgpvpn_dashboard.dashboards.project.bgpvpn import workflows \ as bgpvpn_workflows class IndexView(tables.DataTableView): table_class = bgpvpn_tables.BgpvpnTable template_name = 'project/bgpvpn/index.html' page_title = _("BGP VPNs") @memoized.memoized_method def get_data(self): tenant_id = self.request.user.tenant_id bgpvpns_list = bgpvpn_api.bgpvpns_list( self.request, tenant_id=tenant_id) for bgpvpn in bgpvpns_list: bgpvpn.networks = [api.neutron.network_get( self.request, id, expand_subnet=False) for id in bgpvpn.networks] bgpvpn.routers = [api.neutron.router_get(self.request, id) for id in bgpvpn.routers] return bgpvpns_list class EditDataView(forms.ModalFormView): form_class = bgpvpn_forms.EditDataBgpVpn form_id = "edit_data_bgpvpn_form" modal_header = _("Edit BGPVPN") submit_label = _("Update Change") submit_url = 'horizon:project:bgpvpn:edit' success_url = reverse_lazy('horizon:project:bgpvpn:index') template_name = 'project/bgpvpn/modify.html' page_title = _("Edit BGPVPN") @staticmethod def _join_rts(route_targets_list): return ','.join(route_targets_list) def get_context_data(self, **kwargs): context = super(EditDataView, self).get_context_data(**kwargs) args = (self.kwargs['bgpvpn_id'],) context["bgpvpn_id"] = self.kwargs['bgpvpn_id'] context["submit_url"] = reverse(self.submit_url, args=args) return context def get_initial(self): bgpvpn_id = self.kwargs['bgpvpn_id'] try: # Get initial bgpvpn information bgpvpn = bgpvpn_api.bgpvpn_get(self.request, bgpvpn_id) except Exception: exceptions.handle( self.request, _('Unable to retrieve BGPVPN details.'), redirect=self.success_url) else: data = bgpvpn.to_dict() if self.request.user.is_superuser: for attribute in bgpvpn_common.RT_FORMAT_ATTRIBUTES: data[attribute] = self._join_rts(bgpvpn[attribute]) data['bgpvpn_id'] = data.pop('id') return data class UpdateAssociationsView(workflows.WorkflowView): workflow_class = bgpvpn_workflows.UpdateBgpVpnAssociations page_title = _("Edit BGPVPN associations") failure_url = reverse_lazy("horizon:project:bgpvpn:index") def get_initial(self): bgpvpn_id = self.kwargs['bgpvpn_id'] try: # Get initial bgpvpn information bgpvpn = bgpvpn_api.bgpvpn_get(self.request, bgpvpn_id) data = bgpvpn.to_dict() data['bgpvpn_id'] = data.pop('id') return data except Exception: exceptions.handle( self.request, _('Unable to retrieve BGPVPN details.'), redirect=self.failure_url) class DetailProjectView(views.HorizonTemplateView): template_name = 'project/bgpvpn/detail.html' page_title = "{{ bgpvpn.name }}" redirect_url = 'horizon:project:bgpvpn:index' def get_context_data(self, **kwargs): context = super(DetailProjectView, self).get_context_data(**kwargs) bgpvpn = self.get_data() table = bgpvpn_tables.BgpvpnTable(self.request) context["bgpvpn"] = bgpvpn context["url"] = reverse(self.redirect_url) context["actions"] = table.render_row_actions(bgpvpn) return context @memoized.memoized_method def get_data(self): try: bgpvpn_id = self.kwargs['bgpvpn_id'] return bgpvpn_api.bgpvpn_get(self.request, bgpvpn_id) except Exception: exceptions.handle(self.request, _('Unable to retrieve BGPVPN details.'), redirect=reverse(self.redirect_url)) networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/0000775000175100017510000000000013245511747027612 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/0000775000175100017510000000000013245511747031106 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_networks.htmlnetworking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_netw0000777000175100017510000000064213245511235034343 0ustar zuulzuul00000000000000{% load i18n %}
{% if bgpvpn.networks|length > 1 %} {% trans "Associated Networks" %} {% else %} {% trans "Associated Network" %} {% endif %}
{% for network in bgpvpn.networks %}
{% url 'horizon:project:networks:detail' network as network_url %} {{ network }}
{% endfor %}././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_routers.htmlnetworking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_rout0000777000175100017510000000062013245511235034353 0ustar zuulzuul00000000000000{% load i18n %}
{% if bgpvpn.routers|length > 1 %} {% trans "Associated Routers" %} {% else %} {% trans "Associated Router" %} {% endif %}
{% for router in bgpvpn.routers %}
{% url 'horizon:project:routers:detail' router as router_url %} {{ router }}
{% endfor %}networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_modify.html0000777000175100017510000000035413245511235033421 0ustar zuulzuul00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "You may update the editable properties of your BGP VPN here." %}

{% endblock %}networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/modify.html0000777000175100017510000000027313245511235033262 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update BGPVPN" %}{% endblock %} {% block main %} {% include "project/bgpvpn/_modify.html" %} {% endblock %} networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/detail.html0000777000175100017510000000056413245511235033240 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "BGPVPN Details" %}{% endblock %} {% block page_header %} {% include "horizon/common/_detail_header.html" %} {% endblock %} {% block main %}
{% include "project/bgpvpn/_detail_overview.html" %}
{% endblock %} networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/index.html0000777000175100017510000000023313245511235033076 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "BGP VPNs" %}{% endblock %} {% block main %} {{ table.render }} {% endblock %}././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_detail_overview.htmlnetworking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_detail_overview0000777000175100017510000000064113245511235034356 0ustar zuulzuul00000000000000{% load i18n %}
{% trans "BGPVPN Name" %}
{{ bgpvpn.name }}
{% trans "BGPVPN ID" %}
{{ bgpvpn.id }}
{% trans "Type" %}
{{ bgpvpn.type }}
{% include "project/bgpvpn/_associated_networks.html" %} {% include "project/bgpvpn/_associated_routers.html" %}
networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/urls.py0000777000175100017510000000222413245511235027150 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.conf.urls import url import bgpvpn_dashboard.dashboards.project.bgpvpn.views as bgpvpn_views BGPVPN = r'^(?P[^/]+)/%s$' urlpatterns = [ url(r'^$', bgpvpn_views.IndexView.as_view(), name='index'), url(BGPVPN % 'edit', bgpvpn_views.EditDataView.as_view(), name='edit'), url(BGPVPN % 'update-associations', bgpvpn_views.UpdateAssociationsView.as_view(), name='update-associations'), url(r'^(?P[^/]+)/detail/$', bgpvpn_views.DetailProjectView.as_view(), name='detail'), ] networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/panel.py0000777000175100017510000000144613245511235027267 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.utils.translation import ugettext_lazy as _ import horizon class BGPVPNInterconnections(horizon.Panel): name = _("BGPVPN Interconnections") slug = "bgpvpn" networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/__init__.py0000777000175100017510000000000013245511235027710 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/workflows.py0000777000175100017510000002512713245511235030227 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import workflows from openstack_dashboard import api from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api class UpdateAssociations(workflows.MembershipAction): def __init__(self, request, resource_type, *args, **kwargs): super(UpdateAssociations, self).__init__(request, *args, **kwargs) err_msg = _('Something went wrong when retrieving the list of ' 'associations') if resource_type == 'router': err_msg = _('Unable to retrieve list of routers. ' 'Please try again later.') elif resource_type == 'network': err_msg = _('Unable to retrieve list of networks. ' 'Please try again later.') context = args[0] default_role_field_name = self.get_default_role_field_name() self.fields[default_role_field_name] = forms.CharField(required=False) self.fields[default_role_field_name].initial = 'member' field_name = self.get_member_field_name('member') self.fields[field_name] = forms.MultipleChoiceField(required=False) all_resources = self._get_resources(request, context, resource_type, err_msg) resources_list = [(resource.id, resource.name_or_id) for resource in all_resources] self.fields[field_name].choices = resources_list bgpvpn_id = context.get('bgpvpn_id') try: if bgpvpn_id: associations = [] list_method = getattr(bgpvpn_api, '%s_association_list' % resource_type) associations = [ getattr(association, '%s_id' % resource_type) for association in list_method(request, bgpvpn_id) ] except Exception: exceptions.handle(request, err_msg) self.fields[field_name].initial = associations def _get_resources(self, request, context, resource_type, err_msg): """Get list of available resources.""" # when an admin user uses the project panel BGPVPN, there is no # tenant_id in context because bgpvpn_get doesn't return it if request.user.is_superuser and context.get('tenant_id'): tenant_id = context.get('tenant_id') else: tenant_id = self.request.user.tenant_id try: if resource_type == 'router': return api.neutron.router_list(request, tenant_id=tenant_id) elif resource_type == 'network': return api.neutron.network_list_for_tenant(request, tenant_id) else: raise Exception( _('Resource type not supported: %s') % resource_type) except Exception: exceptions.handle(request, err_msg % resource_type) class UpdateBgpVpnRoutersAction(UpdateAssociations): def __init__(self, request, *args, **kwargs): super(UpdateBgpVpnRoutersAction, self).__init__(request, 'router', *args, **kwargs) class Meta(object): name = _("Router Associations") slug = "update_bgpvpn_router" class UpdateBgpVpnRouters(workflows.UpdateMembersStep): action_class = UpdateBgpVpnRoutersAction help_text = _("Select the routers to be associated.") available_list_title = _("All Routers") members_list_title = _("Selected Routers") no_available_text = _("No router found.") no_members_text = _("No router selected.") show_roles = False depends_on = ("bgpvpn_id", "name") contributes = ("routers_association",) def contribute(self, data, context): if data: member_field_name = self.get_member_field_name('member') context['routers_association'] = data.get(member_field_name, []) return context class UpdateBgpVpnNetworksAction(UpdateAssociations): def __init__(self, request, *args, **kwargs): super(UpdateBgpVpnNetworksAction, self).__init__(request, 'network', *args, **kwargs) class Meta(object): name = _("Network Associations") slug = "update_bgpvpn_network" class UpdateBgpVpnNetworks(workflows.UpdateMembersStep): action_class = UpdateBgpVpnNetworksAction help_text = _("Select the networks to be associated.") available_list_title = _("All Networks") members_list_title = _("Selected Networks") no_available_text = _("No network found.") no_members_text = _("No network selected.") show_roles = False depends_on = ("bgpvpn_id", "name") contributes = ("networks_association",) def contribute(self, data, context): if data: member_field_name = self.get_member_field_name('member') context['networks_association'] = data.get(member_field_name, []) return context class UpdateBgpVpnAssociations(workflows.Workflow): slug = "update_bgpvpn_associations" name = _("Edit BGPVPN associations") finalize_button_name = _("Save") success_message = _('Modified BGPVPN associations "%s".') failure_message = _('Unable to modify BGPVPN associations "%s".') success_url = "horizon:project:bgpvpn:index" default_steps = (UpdateBgpVpnNetworks, UpdateBgpVpnRouters) def format_status_message(self, message): return message % self.context['name'] def _handle_type(self, request, data, association_type): list_method = getattr(bgpvpn_api, '%s_association_list' % association_type) associations = data["%ss_association" % association_type] error_msg = 'Unable to retrieve associations' try: old_associations = [ getattr(association, '%s_id' % association_type) for association in list_method(request, data['bgpvpn_id'])] except Exception: if association_type == 'router': error_msg = _('Unable to retrieve router associations') elif association_type == 'network': error_msg = _('Unable to retrieve network associations') exceptions.handle(request, error_msg) raise to_remove_associations = list(set(old_associations) - set(associations)) to_add_associations = list(set(associations) - set(old_associations)) # If new resource added to the list if len(to_add_associations) > 0: for resource in to_add_associations: error_msg = _('Unable to associate resource %s') % resource try: create_asso = getattr(bgpvpn_api, '%s_association_create' % association_type) params = self._set_params(data, association_type, resource) create_asso(request, data['bgpvpn_id'], **params) except Exception as e: if association_type == 'router': error_msg = _( 'Unable to associate router {}: {}').format( resource, str(e)) elif association_type == 'network': error_msg = _( 'Unable to associate network {}: {}').format( resource, str(e)) exceptions.handle(request, error_msg) raise # If resource has been deleted from the list if len(to_remove_associations) > 0: for resource in to_remove_associations: try: list_method = getattr(bgpvpn_api, '%s_association_list' % association_type) asso_list = list_method(request, data['bgpvpn_id']) for association in asso_list: if getattr(association, '%s_id' % association_type) == resource: delete_method = getattr(bgpvpn_api, '%s_association_delete' % association_type) delete_method(request, association.id, data['bgpvpn_id']) except Exception: if association_type == 'router': error_msg = _('Unable to disassociate router') elif association_type == 'network': error_msg = _('Unable to disassociate network') exceptions.handle(request, error_msg) raise def _set_params(self, data, association_type, resource): params = dict() params['%s_id' % association_type] = resource return params def handle(self, request, data): action = False try: if 'networks_association' in data: self._handle_type(request, data, 'network') action = True if 'routers_association' in data: self._handle_type(request, data, 'router') action = True if not action: raise Exception(_('Associations type is not supported')) except Exception: return False return True networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/forms.py0000777000175100017510000000771613245511235027324 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import collections import logging from django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import messages from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.common import bgpvpn as bgpvpn_common LOG = logging.getLogger(__name__) class CommonData(forms.SelfHandlingForm): fields_order = [] name = forms.CharField(max_length=255, label=_("Name"), required=False) failure_url = reverse_lazy('horizon:project:bgpvpn:index') def __init__(self, request, *args, **kwargs): super(CommonData, self).__init__(request, *args, **kwargs) if 'keyOrder' in self.fields: self.fields.keyOrder = self.fields_order else: self.fields = collections.OrderedDict( (k, self.fields[k]) for k in self.fields_order) @staticmethod def _del_attributes(attributes, data): for attribute in attributes: del data[attribute] def handle(self, request, data): params = {} for key in bgpvpn_common.RT_FORMAT_ATTRIBUTES: if key in data: params[key] = bgpvpn_common.format_rt(data.pop(key, None)) params.update(data) error_msg = _('Something went wrong with BGPVPN %s') % data['name'] try: if self.action == 'update': error_msg = _('Failed to update BGPVPN %s') % data['name'] # attribute tenant_id is required in request when admin user is # logged and bgpvpn form from admin menu is used if request.user.is_superuser and data.get('tenant_id'): attributes = ('bgpvpn_id', 'type', 'tenant_id') # attribute tenant_id does not exist in request # when non-admin user is logged else: attributes = ('bgpvpn_id', 'type') self._del_attributes(attributes, params) bgpvpn = bgpvpn_api.bgpvpn_update(request, data['bgpvpn_id'], **params) msg = _('BGPVPN %s was successfully updated.') % data['name'] elif self.action == 'create': error_msg = _('Failed to create BGPVPN %s') % data['name'] bgpvpn = bgpvpn_api.bgpvpn_create(request, **params) msg = _('BGPVPN %s was successfully created.') % data['name'] else: raise Exception( _('Unsupported action type: %s') % self.action) LOG.debug(msg) messages.success(request, msg) return bgpvpn except Exception: exceptions.handle(request, error_msg, redirect=self.failure_url) return False class EditDataBgpVpn(CommonData): bgpvpn_id = forms.CharField(label=_("ID"), widget=forms.TextInput( attrs={'readonly': 'readonly'})) type = forms.CharField(label=_("Type"), widget=forms.TextInput( attrs={'readonly': 'readonly'})) fields_order = ['name', 'bgpvpn_id', 'type'] def __init__(self, request, *args, **kwargs): super(EditDataBgpVpn, self).__init__(request, *args, **kwargs) self.action = 'update' networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/tables.py0000777000175100017510000000651113245511235027440 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.core.urlresolvers import reverse from django.utils import html from django.utils.http import urlencode from django.utils import safestring from django.utils.translation import ugettext_lazy as _ from horizon import tables class EditInfoBgpVpn(tables.LinkAction): name = "update_info" verbose_name = _("Edit BGPVPN") url = "horizon:project:bgpvpn:edit" classes = ("ajax-modal",) icon = "pencil" class UpdateNetworkAssociations(tables.LinkAction): name = "update_network_associations" verbose_name = _("Update Network Associations") url = "horizon:project:bgpvpn:update-associations" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, bgpvpn): step = 'update_bgpvpn_network' base_url = reverse(self.url, args=[bgpvpn.id]) param = urlencode({"step": step}) return "?".join([base_url, param]) class UpdateRouterAssociations(tables.LinkAction): name = "update_router_associations" verbose_name = _("Update Router Associations") url = "horizon:project:bgpvpn:update-associations" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, bgpvpn): step = 'update_bgpvpn_router' base_url = reverse(self.url, args=[bgpvpn.id]) param = urlencode({"step": step}) return "?".join([base_url, param]) def get_network_url(network): url = reverse('horizon:project:networks:detail', args=[network.id]) instance = '%s' % (url, html.escape(network.name_or_id)) return instance def get_router_url(router): url = reverse('horizon:project:routers:detail', args=[router.id]) instance = '%s' % (url, html.escape(router.name_or_id)) return instance class NetworksColumn(tables.Column): def get_raw_data(self, bgpvpn): networks = [get_network_url(network) for network in bgpvpn.networks] return safestring.mark_safe(', '.join(networks)) class RoutersColumn(tables.Column): def get_raw_data(self, bgpvpn): routers = [get_router_url(router) for router in bgpvpn.routers] return safestring.mark_safe(', '.join(routers)) class BgpvpnTable(tables.DataTable): name = tables.Column("name_or_id", verbose_name=_("Name"), link=("horizon:project:bgpvpn:detail")) type = tables.Column("type", verbose_name=_("Type")) networks = NetworksColumn("networks", verbose_name=_("Networks")) routers = RoutersColumn("routers", verbose_name=_("Routers")) class Meta(object): name = "bgpvpns" verbose_name = _("BGPVPN") row_actions = (EditInfoBgpVpn, UpdateNetworkAssociations, UpdateRouterAssociations) networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/project/__init__.py0000777000175100017510000000000013245511235026414 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/__init__.py0000777000175100017510000000000013245511235024746 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/0000775000175100017510000000000013245511747023742 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/0000775000175100017510000000000013245511747025236 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/views.py0000777000175100017510000000604113245511235026743 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.core.urlresolvers import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon.utils import memoized from openstack_dashboard import api import bgpvpn_dashboard.api.bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.admin.bgpvpn import forms as bgpvpn_forms from bgpvpn_dashboard.dashboards.admin.bgpvpn import tables as bgpvpn_tables from bgpvpn_dashboard.dashboards.admin.bgpvpn import workflows \ as bgpvpn_workflows from bgpvpn_dashboard.dashboards.project.bgpvpn import views as project_views class IndexView(project_views.IndexView): table_class = bgpvpn_tables.BgpvpnTable @memoized.memoized_method def _get_tenant_name(self, id): try: return api.keystone.tenant_get(self.request, id).name except Exception: msg = _("Unable to retrieve information about the " "tenant %s") % id exceptions.handle(self.request, msg) def get_data(self): bgpvpns_list = bgpvpn_api.bgpvpns_list(self.request) for bgpvpn in bgpvpns_list: bgpvpn.networks = [api.neutron.network_get( self.request, id, expand_subnet=False) for id in bgpvpn.networks] bgpvpn.routers = [api.neutron.router_get(self.request, id) for id in bgpvpn.routers] bgpvpn.tenant_name = self._get_tenant_name(bgpvpn.tenant_id) return bgpvpns_list class CreateView(forms.ModalFormView): form_class = bgpvpn_forms.CreateBgpVpn form_id = "create_bgpvpn_form" template_name = 'admin/bgpvpn/create.html' submit_label = _("Create BGPVPN") success_url = reverse_lazy('horizon:admin:bgpvpn:index') page_title = _("Create BGPVPN") submit_url = reverse_lazy("horizon:admin:bgpvpn:create") class EditDataView(project_views.EditDataView): form_class = bgpvpn_forms.EditDataBgpVpn submit_url = 'horizon:admin:bgpvpn:edit' success_url = reverse_lazy('horizon:admin:bgpvpn:index') class UpdateAssociationsView(project_views.UpdateAssociationsView): workflow_class = bgpvpn_workflows.UpdateBgpVpnAssociations page_title = _("Edit BGPVPN associations") failure_url = reverse_lazy("horizon:admin:bgpvpn:index") class DetailProjectView(project_views.DetailProjectView): template_name = 'admin/bgpvpn/detail.html' redirect_url = 'horizon:admin:bgpvpn:index' networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/0000775000175100017510000000000013245511747027234 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/0000775000175100017510000000000013245511747030530 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/create.html0000777000175100017510000000026213245511235032656 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create BGPVPN" %}{% endblock %} {% block main %} {% include "admin/bgpvpn/_create.html" %} {% endblock %} networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/detail.html0000777000175100017510000000056213245511235032660 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "BGPVPN Details" %}{% endblock %} {% block page_header %} {% include "horizon/common/_detail_header.html" %} {% endblock %} {% block main %}
{% include "admin/bgpvpn/_detail_overview.html" %}
{% endblock %} ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_detail_overview.htmlnetworking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_detail_overview.h0000777000175100017510000000171413245511235034230 0ustar zuulzuul00000000000000{% load i18n %}
{% trans "BGPVPN Name" %}
{{ bgpvpn.name }}
{% trans "BGPVPN ID" %}
{{ bgpvpn.id }}
{% trans "Type" %}
{{ bgpvpn.type }}
{% trans "Route Targets" %}
{% for rt in bgpvpn.route_targets %} {{ rt }} {% endfor %}
{% trans "Import Targets" %}
{% for it in bgpvpn.import_targets %} {{ it }} {% endfor %}
{% trans "Export Targets" %}
{% for et in bgpvpn.export_targets %} {{ et }} {% endfor %}
{% trans "Project ID" %}
{{ bgpvpn.tenant_id }}
{% include "project/bgpvpn/_associated_networks.html" %} {% include "project/bgpvpn/_associated_routers.html" %}
networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_create.html0000777000175100017510000000054513245511235033021 0ustar zuulzuul00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body %}
{% include "horizon/common/_form_fields.html" %}

{% trans "Description:" %}

{% trans "Create a new BGP VPN for any project as you need."%}

{% endblock %}networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/urls.py0000777000175100017510000000233313245511235026573 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.conf.urls import url import bgpvpn_dashboard.dashboards.admin.bgpvpn.views as bgpvpn_views BGPVPN = r'^(?P[^/]+)/%s$' urlpatterns = [ url(r'^$', bgpvpn_views.IndexView.as_view(), name='index'), url(r'^create/$', bgpvpn_views.CreateView.as_view(), name='create'), url(BGPVPN % 'edit', bgpvpn_views.EditDataView.as_view(), name='edit'), url(BGPVPN % 'update-associations', bgpvpn_views.UpdateAssociationsView.as_view(), name='update-associations'), url(r'^(?P[^/]+)/detail/$', bgpvpn_views.DetailProjectView.as_view(), name='detail'), ] networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/panel.py0000777000175100017510000000144613245511235026711 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.utils.translation import ugettext_lazy as _ import horizon class BGPVPNInterconnections(horizon.Panel): name = _("BGPVPN Interconnections") slug = "bgpvpn" networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/__init__.py0000777000175100017510000000000013245511235027332 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/workflows.py0000777000175100017510000000317013245511235027643 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 bgpvpn_dashboard.dashboards.project.bgpvpn import workflows \ as project_workflows class UpdateBgpVpnRouters(project_workflows.UpdateBgpVpnRouters): action_class = project_workflows.UpdateBgpVpnRoutersAction depends_on = ("bgpvpn_id", "tenant_id", "name") class UpdateBgpVpnNetworks(project_workflows.UpdateBgpVpnNetworks): action_class = project_workflows.UpdateBgpVpnNetworksAction depends_on = ("bgpvpn_id", "tenant_id", "name") class UpdateBgpVpnAssociations(project_workflows.UpdateBgpVpnAssociations): success_url = "horizon:admin:bgpvpn:index" default_steps = (UpdateBgpVpnNetworks, UpdateBgpVpnRouters) def _set_params(self, data, association_type, resource): params = super( UpdateBgpVpnAssociations, self)._set_params(data, association_type, resource) params['tenant_id'] = data['tenant_id'] return params networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/forms.py0000777000175100017510000001004013245511235026726 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 django.core.urlresolvers import reverse_lazy from django.core.validators import RegexValidator from django.utils.translation import ugettext_lazy as _ from horizon import forms from openstack_dashboard import api from bgpvpn_dashboard.common import bgpvpn as bgpvpn_common from networking_bgpvpn.neutron.services.common import constants from bgpvpn_dashboard.dashboards.project.bgpvpn import forms \ as project_forms if constants.RTRD_REGEX[0] == '^' and constants.RTRD_REGEX[-1] == '$': RTRD_REGEX = constants.RTRD_REGEX[1:-1] else: msg = _("Bug, inconsistency between neutron-lib and " "networking-bgpvpn for RTRD regex") raise Exception(msg) RTRDS_REGEX = '^%s( *, *%s)*$' % (RTRD_REGEX, RTRD_REGEX) class CommonData(project_forms.CommonData): route_targets = forms.CharField( max_length=255, validators=[RegexValidator(regex=RTRDS_REGEX, message=_("Route targets is not valid"))], label=_("Route targets"), required=False, help_text=bgpvpn_common.ROUTE_TARGET_HELP) import_targets = forms.CharField( max_length=255, validators=[RegexValidator(regex=RTRDS_REGEX, message=_("Import targets is not valid"))], label=_("Import targets"), required=False, help_text=bgpvpn_common.ROUTE_TARGET_HELP + ' To use only on import.') export_targets = forms.CharField( max_length=255, validators=[RegexValidator(regex=RTRDS_REGEX, message=_("Export targets is not valid"))], label=_("Export targets"), required=False, help_text=bgpvpn_common.ROUTE_TARGET_HELP + ' To use only on export.') failure_url = reverse_lazy('horizon:admin:bgpvpn:index') def __init__(self, request, *args, **kwargs): super(CommonData, self).__init__(request, *args, **kwargs) class CreateBgpVpn(CommonData): tenant_id = forms.ChoiceField(label=_("Project")) type = forms.ChoiceField(choices=[("l3", _('l3')), ("l2", _('l2'))], label=_("Type"), help_text=_("The type of VPN " " and the technology behind it.")) fields_order = ['name', 'tenant_id', 'type', 'route_targets', 'import_targets', 'export_targets'] def __init__(self, request, *args, **kwargs): super(CreateBgpVpn, self).__init__(request, *args, **kwargs) tenant_choices = [('', _("Select a project"))] tenants, has_more = api.keystone.tenant_list(request) for tenant in tenants: if tenant.enabled: tenant_choices.append((tenant.id, tenant.name)) self.fields['tenant_id'].choices = tenant_choices self.action = 'create' class EditDataBgpVpn(CommonData): bgpvpn_id = forms.CharField(label=_("ID"), widget=forms.TextInput( attrs={'readonly': 'readonly'})) type = forms.CharField(label=_("Type"), widget=forms.TextInput( attrs={'readonly': 'readonly'})) tenant_id = forms.CharField(widget=forms.HiddenInput()) fields_order = ['name', 'bgpvpn_id', 'tenant_id', 'type', 'route_targets', 'import_targets', 'export_targets'] def __init__(self, request, *args, **kwargs): super(EditDataBgpVpn, self).__init__(request, *args, **kwargs) self.action = 'update' networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/tables.py0000777000175100017510000001025013245511235027055 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import logging from django.core.urlresolvers import reverse from django.utils import html from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy from horizon import exceptions from horizon import tables from openstack_dashboard import policy from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn import tables as project_tables LOG = logging.getLogger(__name__) class DeleteBgpvpn(policy.PolicyTargetMixin, tables.DeleteAction): @staticmethod def action_present(count): return ungettext_lazy(u"Delete BGPVPN", u"Delete BGPVPNs", count) @staticmethod def action_past(count): return ungettext_lazy(u"Deleted BGPVPN", u"Deleted BGPVPNs", count) def delete(self, request, obj_id): try: bgpvpn_api.bgpvpn_delete(request, obj_id) except Exception: msg = _('Failed to delete BGPVPN %s') % obj_id LOG.info(msg) redirect = reverse('horizon:admin:bgpvpn:index') exceptions.handle(request, msg, redirect=redirect) class CreateBgpVpn(tables.LinkAction): name = "create" verbose_name = _("Create BGPVPN") url = "horizon:admin:bgpvpn:create" classes = ("ajax-modal",) icon = "plus" class EditInfoBgpVpn(project_tables.EditInfoBgpVpn): url = "horizon:admin:bgpvpn:edit" class UpdateNetworkAssociations(project_tables.UpdateNetworkAssociations): url = "horizon:admin:bgpvpn:update-associations" class UpdateRouterAssociations(project_tables.UpdateRouterAssociations): url = "horizon:admin:bgpvpn:update-associations" def get_route_targets(bgpvpn): return ', '.join(rt for rt in bgpvpn.route_targets) def get_import_targets(bgpvpn): return ', '.join(it for it in bgpvpn.import_targets) def get_export_targets(bgpvpn): return ', '.join(et for et in bgpvpn.export_targets) def get_network_url(network): url = reverse('horizon:admin:networks:detail', args=[network.id]) instance = '%s' % (url, html.escape(network.name_or_id)) return instance def get_router_url(router): url = reverse('horizon:admin:routers:detail', args=[router.id]) instance = '%s' % (url, html.escape(router.name_or_id)) return instance class BgpvpnTable(tables.DataTable): tenant_id = tables.Column("tenant_name", verbose_name=_("Project")) name = tables.Column("name_or_id", verbose_name=_("Name"), link=("horizon:admin:bgpvpn:detail")) type = tables.Column("type", verbose_name=_("Type")) route_targets = tables.Column(get_route_targets, verbose_name=_("Route Targets")) import_targets = tables.Column(get_import_targets, verbose_name=_("Import Targets")) export_targets = tables.Column(get_export_targets, verbose_name=_("Export Targets")) networks = project_tables.NetworksColumn("networks", verbose_name=_("Networks")) routers = project_tables.RoutersColumn("routers", verbose_name=_("Routers")) class Meta(object): table_actions = (CreateBgpVpn, DeleteBgpvpn) row_actions = (EditInfoBgpVpn, UpdateNetworkAssociations, UpdateRouterAssociations, DeleteBgpvpn) networking-bgpvpn-8.0.0/bgpvpn_dashboard/dashboards/admin/__init__.py0000777000175100017510000000000013245511235026036 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/common/0000775000175100017510000000000013245511747022030 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/common/__init__.py0000777000175100017510000000000013245511235024124 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/common/bgpvpn.py0000777000175100017510000000211713245511235023674 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import re from django.utils.translation import ugettext_lazy as _ ROUTE_TARGET_HELP = _("A single BGP Route Target or a " "comma-separated list of BGP Route Target. Example: " "64512:1 or 64512:1,64512:2,64512:3") RT_FORMAT_ATTRIBUTES = ('route_targets', 'import_targets', 'export_targets') def format_rt(route_targets): if route_targets: return re.compile(" *, *").split(route_targets) else: return [] networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/0000775000175100017510000000000013245511747021517 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/test_data/0000775000175100017510000000000013245511747023467 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/test_data/bgpvpn_data.py0000777000175100017510000000400313245511235026320 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 copy from bgpvpn_dashboard.api import bgpvpn from openstack_dashboard.test.test_data import utils def data(TEST): TEST.bgpvpns = utils.TestDataContainer() TEST.api_bgpvpns = utils.TestDataContainer() TEST.network_associations = utils.TestDataContainer() TEST.api_network_associations = utils.TestDataContainer() TEST.router_associations = utils.TestDataContainer() TEST.api_router_associations = utils.TestDataContainer() bgpvpn_dict = {'id': 'b595e758-1877-4aec-92a2-6834d76f1025', 'tenant_id': '1', 'name': 'bgpvpn1', 'route_targets': '64500:1' } TEST.api_bgpvpns.add(bgpvpn_dict) b = bgpvpn.Bgpvpn(copy.deepcopy(bgpvpn_dict)) TEST.bgpvpns.add(b) network_association_dict = { 'id': '99ef096d-21fb-43a7-9e2a-b3c464abef3a', 'network_id': '063cf7f3-ded1-4297-bc4c-31eae876cc91', 'tenant_id': '1'} TEST.api_network_associations.add(network_association_dict) na = bgpvpn.NetworkAssociation(copy.deepcopy(network_association_dict)) TEST.network_associations.add(na) router_association_dict = { 'id': '9736c228-745d-4e78-83a5-d971d9fd8f2c', 'router_id': '279989f7-54bb-41d9-ba42-0d61f12fda61', 'tenant_id': '1'} TEST.api_router_associations.add(router_association_dict) ra = bgpvpn.RouterAssociation(copy.deepcopy(router_association_dict)) TEST.router_associations.add(ra) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/test_data/__init__.py0000777000175100017510000000000013245511235025563 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/test_data/utils.py0000777000175100017510000000223313245511235025176 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 openstack_dashboard.test.test_data import utils def load_test_data(load_onto=None): from openstack_dashboard.test.test_data import exceptions from openstack_dashboard.test.test_data import neutron_data from bgpvpn_dashboard.test.test_data import bgpvpn_data # The order of these loaders matters, some depend on others. loaders = ( exceptions.data, neutron_data.data, bgpvpn_data.data, ) if load_onto: for data_func in loaders: data_func(load_onto) return load_onto else: return utils.TestData(*loaders) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/urls.py0000777000175100017510000000125413245511235023055 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 django.conf import urls import openstack_dashboard.urls urlpatterns = [ urls.url(r'', urls.include(openstack_dashboard.urls)) ] networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/settings.py0000777000175100017510000000244113245511235023727 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. # Default to Horizons test settings to avoid any missing keys from horizon.test.settings import * # noqa from openstack_dashboard.test.settings import * # noqa # pop these keys to avoid log warnings about deprecation # update_dashboards will populate them anyway HORIZON_CONFIG.pop('dashboards', None) HORIZON_CONFIG.pop('default_dashboard', None) import bgpvpn_dashboard.enabled import openstack_dashboard.enabled from openstack_dashboard.utils import settings settings.update_dashboards( [ bgpvpn_dashboard.enabled, openstack_dashboard.enabled, ], HORIZON_CONFIG, INSTALLED_APPS ) # Ensure any duplicate apps are removed after the update_dashboards call INSTALLED_APPS = list(set(INSTALLED_APPS)) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/project/0000775000175100017510000000000013245511747023165 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/project/test_tables.py0000666000175100017510000001132313245511235026042 0ustar zuulzuul00000000000000# Copyright (c) 2017 Orange. # 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 testtools from django.utils.translation import ugettext_lazy as _ from bgpvpn_dashboard.dashboards.project.bgpvpn import tables as bgpvpn_tables class TestUpdateNetworkAssociations(testtools.TestCase): def setUp(self): super(TestUpdateNetworkAssociations, self).setUp() self.update_net_assocs = bgpvpn_tables.UpdateNetworkAssociations() self.assertEqual('horizon:project:bgpvpn:update-associations', self.update_net_assocs.url) @mock.patch.object(bgpvpn_tables, 'reverse') def test_get_link_url(self, mock_reverse): mock_reverse.return_value = 'foo_reverse_url' mock_bgpvpn = mock.Mock(id='foo-id') result = self.update_net_assocs.get_link_url(mock_bgpvpn) mock_reverse.assert_called_once_with( 'horizon:project:bgpvpn:update-associations', args=['foo-id']) self.assertEqual('foo_reverse_url?step=update_bgpvpn_network', result) class TestUpdateRouterAssociations(testtools.TestCase): def setUp(self): super(TestUpdateRouterAssociations, self).setUp() self.update_net_assocs = bgpvpn_tables.UpdateRouterAssociations() self.assertEqual('horizon:project:bgpvpn:update-associations', self.update_net_assocs.url) @mock.patch.object(bgpvpn_tables, 'reverse') def test_get_link_url(self, mock_reverse): mock_reverse.return_value = 'foo_reverse_url' mock_bgpvpn = mock.Mock(id='foo-id') result = self.update_net_assocs.get_link_url(mock_bgpvpn) mock_reverse.assert_called_once_with( 'horizon:project:bgpvpn:update-associations', args=['foo-id']) self.assertEqual('foo_reverse_url?step=update_bgpvpn_router', result) class TestFunctionGet(testtools.TestCase): @mock.patch.object(bgpvpn_tables, 'reverse') def test_get_network_url(self, mock_reverse): mock_reverse.return_value = 'foo_reverse_url' mock_network = mock.Mock(id='foo-id', name_or_id="foo") result = bgpvpn_tables.get_network_url(mock_network) mock_reverse.assert_called_once_with( 'horizon:project:networks:detail', args=['foo-id']) self.assertEqual('foo', result) @mock.patch.object(bgpvpn_tables, 'reverse') def test_get_router_url(self, mock_reverse): mock_reverse.return_value = 'foo_reverse_url' mock_network = mock.Mock(id='foo-id', name_or_id="foo") result = bgpvpn_tables.get_router_url(mock_network) mock_reverse.assert_called_once_with( 'horizon:project:routers:detail', args=['foo-id']) self.assertEqual('foo', result) class TestNetworksColumn(testtools.TestCase): def setUp(self): super(TestNetworksColumn, self).setUp() self.nets_column = bgpvpn_tables.NetworksColumn( "networks", verbose_name=_("Networks")) def test_get_raw_data(self): result_expected = "foo1, " \ "foo2" mock_net1 = mock.Mock(id="id1", name_or_id="foo1") mock_net2 = mock.Mock(id="id2", name_or_id="foo2") networks = [mock_net1, mock_net2] mock_bgpvpn = mock.Mock(networks=networks) result = self.nets_column.get_raw_data(mock_bgpvpn) self.assertEqual(result_expected, result) class TestRoutersColumn(testtools.TestCase): def setUp(self): super(TestRoutersColumn, self).setUp() self.routers_column = bgpvpn_tables.RoutersColumn( "routers", verbose_name=_("Routers")) def test_get_raw_data(self): result_expected = "foo1, " \ "foo2" mock_router1 = mock.Mock(id="id1", name_or_id="foo1") mock_router2 = mock.Mock(id="id2", name_or_id="foo2") routers = [mock_router1, mock_router2] mock_bgpvpn = mock.Mock(routers=routers) result = self.routers_column.get_raw_data(mock_bgpvpn) self.assertEqual(result_expected, result) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/project/__init__.py0000777000175100017510000000000013245511235025261 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/project/test_views.py0000666000175100017510000002151513245511235025731 0ustar zuulzuul00000000000000# Copyright (c) 2017 Orange. # # 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 django.core.urlresolvers import reverse from django.core.urlresolvers import reverse_lazy from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn import forms as bgpvpn_form from bgpvpn_dashboard.dashboards.project.bgpvpn import tables as bgpvpn_tables from bgpvpn_dashboard.dashboards.project.bgpvpn import views as bgpvpn_views from bgpvpn_dashboard.dashboards.project.bgpvpn import workflows as \ bgpvpn_workflows from openstack_dashboard.test import helpers class TestIndexView(helpers.APITestCase): def setUp(self): super(TestIndexView, self).setUp() mock_request = mock.Mock(horizon={'async_messages': []}) self.bgpvpn_view = bgpvpn_views.IndexView(request=mock_request) self.bgpvpn_view._prev = False self.bgpvpn_view._more = False self.assertEqual(bgpvpn_tables.BgpvpnTable, self.bgpvpn_view.table_class) self.assertEqual('project/bgpvpn/index.html', self.bgpvpn_view.template_name) def _get_mock_bgpvpn(self, prefix): bgpvpn_info = {} if prefix: bgpvpn_info = { "name": "%s_name" % prefix, "route_targets": [], "import_targets": [], "export_targets": [], "networks": [], "routers": [], "tenant_id": "tenant_id", "type": "l3" } return bgpvpn_api.Bgpvpn(bgpvpn_info) @mock.patch.object(bgpvpn_views, 'bgpvpn_api', autospec=True) def test_get_data(self, mock_bgpvpn_api): """Test that get_data works.""" bgpvpn_foo = self._get_mock_bgpvpn("foo") bgpvpn_bar = self._get_mock_bgpvpn("bar") mock_bgpvpn_api.bgpvpns_list.return_value = [bgpvpn_bar, bgpvpn_foo] result = self.bgpvpn_view.get_data() expected_bgpvpns = [bgpvpn_bar, bgpvpn_foo] self.assertEqual(expected_bgpvpns, result) class TestEditDataView(helpers.APITestCase): def setUp(self): super(TestEditDataView, self).setUp() mock_request = mock.Mock(horizon={'async_messages': []}) self.bgpvpn_view = bgpvpn_views.EditDataView(request=mock_request) fake_response = {'status_code': 200} self.mock_request = mock.Mock(return_value=fake_response, META=[]) self.bgpvpn_view.request = self.mock_request self.bgpvpn_view.kwargs = {'bgpvpn_id': 'foo-id'} self.assertEqual(bgpvpn_form.EditDataBgpVpn, self.bgpvpn_view.form_class) self.assertEqual('horizon:project:bgpvpn:edit', self.bgpvpn_view.submit_url) self.assertEqual(reverse_lazy('horizon:project:bgpvpn:index'), self.bgpvpn_view.success_url) self.assertEqual('project/bgpvpn/modify.html', self.bgpvpn_view.template_name) @mock.patch.object(bgpvpn_views, 'bgpvpn_api', autospec=True) def test_get_initial_user(self, mock_bgpvpn_api): self.bgpvpn_view.request.user.is_superuser = False bgpvpn_data = {"name": "foo-name", "id": "foo-id", "type": "l3"} expected_data = {"name": "foo-name", "bgpvpn_id": "foo-id", "type": "l3"} mock_bgpvpn_api.bgpvpn_get.return_value = bgpvpn_api.Bgpvpn( bgpvpn_data) result = self.bgpvpn_view.get_initial() mock_bgpvpn_api.bgpvpn_get.assert_called_once_with( self.bgpvpn_view.request, "foo-id") for key, val in expected_data.items(): self.assertEqual(val, result[key]) @mock.patch.object(bgpvpn_views, 'bgpvpn_api', autospec=True) def test_get_initial_admin(self, mock_bgpvpn_api): self.bgpvpn_view.request.user.is_superuser = True bgpvpn_data = {"name": "foo-name", "id": "foo-id", "type": "l3", "route_targets": ["65432:1", "65432:2"], "import_targets": [], "export_targets": []} expected_data = {"name": "foo-name", "bgpvpn_id": "foo-id", "type": "l3", "route_targets": "65432:1,65432:2", "import_targets": "", "export_targets": ""} mock_bgpvpn_api.bgpvpn_get.return_value = bgpvpn_api.Bgpvpn( bgpvpn_data) result = self.bgpvpn_view.get_initial() mock_bgpvpn_api.bgpvpn_get.assert_called_once_with( self.bgpvpn_view.request, "foo-id") for key, val in expected_data.items(): self.assertEqual(val, result[key]) def test_get_context_data(self): mock_form = mock.Mock() args = ("foo-id",) expected_context = { 'bgpvpn_id': 'foo-id', 'submit_url': reverse("horizon:project:bgpvpn:edit", args=args)} context = self.bgpvpn_view.get_context_data(form=mock_form) self.assertIn('view', context) self.assertIsInstance(context['view'], bgpvpn_views.EditDataView) for key, val in expected_context.items(): self.assertIn(key, context) self.assertEqual(val, context[key]) def test_join_rts(self): route_targets_list = ["65400:1", "65401:1"] expected_result = "65400:1,65401:1" result = self.bgpvpn_view._join_rts(route_targets_list) self.assertEqual(expected_result, result) class TestUpdateAssociationsView(helpers.APITestCase): def setUp(self): super(TestUpdateAssociationsView, self).setUp() self.bgpvpn_view = bgpvpn_views.UpdateAssociationsView() fake_response = {'status_code': 200} self.mock_request = mock.Mock(return_value=fake_response, META=[]) self.bgpvpn_view.request = self.mock_request self.bgpvpn_view.kwargs = {'bgpvpn_id': 'foo-id'} self.assertEqual(bgpvpn_workflows.UpdateBgpVpnAssociations, self.bgpvpn_view.workflow_class) self.assertEqual(reverse_lazy("horizon:project:bgpvpn:index"), self.bgpvpn_view.failure_url) @mock.patch.object(bgpvpn_views, 'bgpvpn_api', autospec=True) def test_get_initial(self, mock_bgpvpn_api): bgpvpn_data = {"name": "foo-name", "id": "foo-id", "type": "l3"} expected_data = {"name": "foo-name", "bgpvpn_id": "foo-id", "type": "l3"} mock_bgpvpn_api.bgpvpn_get.return_value = bgpvpn_api.Bgpvpn( bgpvpn_data) result = self.bgpvpn_view.get_initial() mock_bgpvpn_api.bgpvpn_get.assert_called_once_with( self.bgpvpn_view.request, "foo-id") for key, val in expected_data.items(): self.assertEqual(val, result[key]) class TestDetailProjectView(helpers.APITestCase): def setUp(self): super(TestDetailProjectView, self).setUp() self.view = bgpvpn_views.DetailProjectView() fake_response = {'status_code': 200} self.mock_request = mock.Mock(return_value=fake_response) self.view.request = self.mock_request self.view.kwargs = {'bgpvpn_id': 'foo-id'} self.assertEqual('project/bgpvpn/detail.html', self.view.template_name) self.assertEqual('{{ bgpvpn.name }}', self.view.page_title) self.assertEqual('horizon:project:bgpvpn:index', self.view.redirect_url) self.addCleanup(mock.patch.stopall) @mock.patch.object(bgpvpn_views, 'bgpvpn_api', autospec=True) def test_get_context_data(self, mock_bgpvpn_api): expected_bgpvpn = bgpvpn_api.Bgpvpn({"id": "foo-id"}) mock_bgpvpn_api.bgpvpn_get.return_value = expected_bgpvpn context = self.view.get_context_data() mock_bgpvpn_api.bgpvpn_get.assert_called_once_with( self.view.request, "foo-id") self.assertIn('view', context) self.assertIn('bgpvpn', context) self.assertIn('url', context) self.assertIsInstance(context['view'], bgpvpn_views.DetailProjectView) self.assertEqual(expected_bgpvpn, context['bgpvpn']) self.assertEqual(reverse('horizon:project:bgpvpn:index'), context['url']) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/project/test_forms.py0000666000175100017510000000350213245511235025716 0ustar zuulzuul00000000000000# Copyright (c) 2017 Orange. # 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 openstack_dashboard.test import helpers from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn import forms as bgpvpn_form class TestEditDataBgpVpn(helpers.APITestCase): def setUp(self): super(TestEditDataBgpVpn, self).setUp() self.mock_request = mock.MagicMock() self.bgpvpn_form = bgpvpn_form.EditDataBgpVpn(self.mock_request) self.bgpvpn_form.action = "update" @mock.patch.object(bgpvpn_form, 'bgpvpn_api') def test_handle(self, mock_bgpvpn_api): self.bgpvpn_form.request.user.is_superuser = False test_data = {"bgpvpn_id": "foo-id", "name": "foo-name", "type": "l3"} expected_data = bgpvpn_api.Bgpvpn({"id": "foo-id", "name": "foo-name", "type": "l3"}) mock_bgpvpn_api.bgpvpn_update.return_value = expected_data result = self.bgpvpn_form.handle(self.mock_request, test_data) self.assertEqual(expected_data, result) mock_bgpvpn_api.bgpvpn_update.assert_called_once_with( self.mock_request, "foo-id", name="foo-name") networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/__init__.py0000777000175100017510000000000013245511235023613 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/helpers.py0000777000175100017510000000224613245511235023534 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 openstack_dashboard.test import helpers from bgpvpn_dashboard.test.test_data import utils def create_stubs(stubs_to_create=None): if stubs_to_create is None: stubs_to_create = {} return helpers.create_stubs(stubs_to_create) class TestCase(helpers.TestCase): def _setup_test_data(self): super(TestCase, self)._setup_test_data() utils.load_test_data(self) class APITestCase(helpers.APITestCase): def _setup_test_data(self): super(APITestCase, self)._setup_test_data() utils.load_test_data(self) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/test.py0000777000175100017510000000133613245511235023050 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 django.conf import urls import openstack_dashboard.urls urlpatterns = urls.patterns( '', urls.url(r'', urls.include(openstack_dashboard.urls)) ) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/admin/0000775000175100017510000000000013245511747022607 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/admin/test_tables.py0000666000175100017510000000220613245511235025464 0ustar zuulzuul00000000000000# Copyright (c) 2017 Orange. # 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 testtools from bgpvpn_dashboard.dashboards.admin.bgpvpn import tables as bgpvpn_tables class TestDeleteBgpvpns(testtools.TestCase): @mock.patch.object(bgpvpn_tables, 'bgpvpn_api') def test_delete(self, mock_bgpvpn_api): mock_request = mock.Mock() delete_bgpvpn = bgpvpn_tables.DeleteBgpvpn() delete_bgpvpn.delete(mock_request, "id") mock_bgpvpn_api.bgpvpn_delete.assert_called_once_with(mock_request, "id") networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/admin/__init__.py0000777000175100017510000000000013245511235024703 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/admin/test_views.py0000666000175100017510000000617313245511235025356 0ustar zuulzuul00000000000000# Copyright (c) 2017 Orange. # # 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 collections import namedtuple from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.admin.bgpvpn import tables as bgpvpn_tables from bgpvpn_dashboard.dashboards.admin.bgpvpn import views as bgpvpn_views from openstack_dashboard.test import helpers VIEWS = "bgpvpn_dashboard.dashboards.admin.bgpvpn.views" class TestIndexView(helpers.APITestCase): def setUp(self): super(TestIndexView, self).setUp() mock_request = mock.Mock(horizon={'async_messages': []}) self.bgpvpn_view = bgpvpn_views.IndexView(request=mock_request) self.assertEqual(bgpvpn_tables.BgpvpnTable, self.bgpvpn_view.table_class) def _get_mock_bgpvpn(self, prefix): bgpvpn_info = {} if prefix: bgpvpn_info = { "name": "%s_name" % prefix, "route_targets": [], "import_targets": [], "export_targets": [], "networks": [], "routers": [], "tenant_id": "tenant_id", "type": "l3" } return bgpvpn_api.Bgpvpn(bgpvpn_info) @mock.patch.object(bgpvpn_views.api, 'keystone', autospec=True) def test_get_tenant_name(self, mock_api): Tenant = namedtuple("Tenant", ["id", "name"]) tenant = Tenant("tenant_id", "tenant_name") mock_api.tenant_get.return_value = tenant result = self.bgpvpn_view._get_tenant_name("tenant_id") mock_api.tenant_get.assert_called_once_with( self.bgpvpn_view.request, "tenant_id") self.assertEqual(result, "tenant_name") @mock.patch('%s.IndexView._get_tenant_name' % VIEWS, return_value={"tenant_id": "tenant_name"}) @mock.patch.object(bgpvpn_views, 'api', autospec=True) @mock.patch.object(bgpvpn_views, 'bgpvpn_api', autospec=True) def test_get_data(self, mock_bgpvpn_api, mock_api, mock_get_tenant_name): bgpvpn_foo = self._get_mock_bgpvpn("foo") bgpvpn_bar = self._get_mock_bgpvpn("bar") mock_neutron_client = mock_api.neutron.neutronclient(mock.Mock()) mock_bgpvpn_api.bgpvpns_list.return_value = [bgpvpn_foo, bgpvpn_bar] mock_neutron_client.list_networks.return_value = [] mock_neutron_client.list_routers.return_value = [] expected_bgpvpns = [bgpvpn_foo, bgpvpn_bar] result = self.bgpvpn_view.get_data() calls = [mock.call("tenant_id"), mock.call("tenant_id")] mock_get_tenant_name.assert_has_calls(calls) self.assertEqual(result, expected_bgpvpns) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/admin/test_forms.py0000666000175100017510000001001013245511235025330 0ustar zuulzuul00000000000000# Copyright (c) 2017 Orange. # 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 openstack_dashboard.test import helpers from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.admin.bgpvpn import forms as bgpvpn_a_form from bgpvpn_dashboard.dashboards.project.bgpvpn import forms as bgpvpn_p_form class Tenant(object): def __init__(self, id, name, enabled): self.id = id self.name = name self.enabled = enabled class TestCreateDataBgpVpn(helpers.APITestCase): @mock.patch.object(bgpvpn_a_form, 'api') def setUp(self, mock_api): super(TestCreateDataBgpVpn, self).setUp() self.mock_request = mock.MagicMock() @mock.patch.object(bgpvpn_p_form, 'bgpvpn_api') @mock.patch.object(bgpvpn_a_form, 'api') def test_invalid_rt(self, mock_api, mock_bgpvpn_api): mock_api.keystone.tenant_list.return_value = [], False mock_bgpvpn_api.list_bgpvpns.return_value = [] data = {"route_targets": "xyz", "import_targets": "0", "export_targets": "64512:1000000000000, xyz"} self.bgpvpn_form = bgpvpn_a_form.CreateBgpVpn(self.mock_request, data) self.assertTrue(self.bgpvpn_form.has_error("route_targets")) self.assertTrue(self.bgpvpn_form.has_error("import_targets")) self.assertTrue(self.bgpvpn_form.has_error("export_targets")) @mock.patch.object(bgpvpn_p_form, 'bgpvpn_api') @mock.patch.object(bgpvpn_a_form, 'api') def test_valid_rt(self, mock_api, mock_bgpvpn_api): mock_api.keystone.tenant_list.return_value = [], False mock_bgpvpn_api.list_bgpvpns.return_value = [] data = {"route_targets": "65421:1", "import_targets": "65421:1, 65421:2", "export_targets": "65421:3"} self.bgpvpn_form = bgpvpn_a_form.CreateBgpVpn(self.mock_request, data) self.assertFalse(self.bgpvpn_form.has_error("route_targets")) self.assertFalse(self.bgpvpn_form.has_error("import_targets")) self.assertFalse(self.bgpvpn_form.has_error("export_targets")) @mock.patch.object(bgpvpn_p_form, 'bgpvpn_api') @mock.patch.object(bgpvpn_a_form, 'api') def test_handle_update(self, mock_api, mock_bgpvpn_api): data = {"bgpvpn_id": "foo-id", "type": "l3", "name": "foo-name", "route_targets": "65421:1", "import_targets": "65421:2", "export_targets": "65421:3"} mock_api.keystone.tenant_list.return_value = [], False self.bgpvpn_form = bgpvpn_a_form.CreateBgpVpn(self.mock_request) self.bgpvpn_form.action = "update" expected_data = bgpvpn_api.Bgpvpn({"bgpvpn_id": "foo-id", "name": "foo-name", "tenant_id": "tenant_id", "route_targets": ["65421:1"], "import_targets": ["65421:2"], "export_targets": ["65421:3"]}) mock_bgpvpn_api.bgpvpn_update.return_value = expected_data result = self.bgpvpn_form.handle(self.mock_request, data) mock_bgpvpn_api.bgpvpn_update.assert_called_once_with( self.mock_request, "foo-id", name="foo-name", route_targets=["65421:1"], import_targets=["65421:2"], export_targets=["65421:3"]) self.assertEqual(result, expected_data) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/api_tests/0000775000175100017510000000000013245511747023512 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/api_tests/bgpvpn_tests.py0000777000175100017510000001576213245511235026612 0ustar zuulzuul00000000000000# Copyright (c) 2016 Orange. # 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 openstack_dashboard.test import helpers as test from bgpvpn_dashboard import api from bgpvpn_dashboard.test import helpers as bgpvpn_test from neutronclient.v2_0.client import Client as neutronclient BGPVPN_OBJECT_PATH = '/bgpvpn/bgpvpns' BGPVPN_RESOURCE_PATH = '/bgpvpn/bgpvpns/%s' BGPVPN_NETWORK_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/network_associations' BGPVPN_ROUTER_ASSOCIATION_PATH = '/bgpvpn/bgpvpns/%s/router_associations' class BgpvpnApiTests(bgpvpn_test.APITestCase): @test.create_stubs({neutronclient: ('list_ext',)}) def test_bgpvpn_list(self): bgpvpns = {'bgpvpns': self.api_bgpvpns.list()} uri = BGPVPN_OBJECT_PATH neutronclient.list_ext('bgpvpns', uri, True).AndReturn(bgpvpns) self.mox.ReplayAll() ret_val = api.bgpvpn.bgpvpns_list(self.request) for n in ret_val: self.assertIsInstance(n, api.bgpvpn.Bgpvpn) @test.create_stubs({neutronclient: ('create_ext',)}) def test_bgpvpn_create(self): bgpvpn = self.bgpvpns.first() data = {'name': bgpvpn.name, 'route_targets': bgpvpn.route_targets, 'tenant_id': bgpvpn.tenant_id} ret_dict = {'bgpvpn': data} uri = BGPVPN_OBJECT_PATH neutronclient.create_ext(uri, ret_dict).AndReturn(ret_dict) self.mox.ReplayAll() ret_val = api.bgpvpn.bgpvpn_create(self.request, **data) self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) @test.create_stubs({neutronclient: ('show_ext',)}) def test_bgpvpn_get(self): bgpvpn = self.bgpvpns.first() ret_dict = {'bgpvpn': self.api_bgpvpns.first()} uri = BGPVPN_RESOURCE_PATH neutronclient.show_ext(uri, bgpvpn.id).AndReturn(ret_dict) self.mox.ReplayAll() ret_val = api.bgpvpn.bgpvpn_get(self.request, bgpvpn.id) self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) @test.create_stubs({neutronclient: ('update_ext',)}) def test_bgpvpn_update(self): bgpvpn = self.bgpvpns.first() bgpvpn_dict = self.api_bgpvpns.first() bgpvpn.name = 'new name' bgpvpn.route_targets = '65001:2' bgpvpn_dict['name'] = 'new name' bgpvpn_dict['route_targets'] = '65001:2' form_data = {'name': bgpvpn.name, 'route_targets': bgpvpn.route_targets} form_dict = {'bgpvpn': form_data} ret_dict = {'bgpvpn': bgpvpn_dict} uri = BGPVPN_RESOURCE_PATH neutronclient.update_ext(uri, bgpvpn.id, form_dict).AndReturn(ret_dict) self.mox.ReplayAll() ret_val = api.bgpvpn.bgpvpn_update( self.request, bgpvpn.id, **form_data) self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) @test.create_stubs({neutronclient: ('delete_ext',)}) def test_bgpvpn_delete(self): bgpvpn = self.bgpvpns.first() api_bgpvpn = {'bgpvpn': self.api_bgpvpns.first()} uri = BGPVPN_RESOURCE_PATH neutronclient.delete_ext(uri, bgpvpn.id).AndReturn(api_bgpvpn) self.mox.ReplayAll() api.bgpvpn.bgpvpn_delete(self.request, bgpvpn.id) @test.create_stubs({neutronclient: ('list_ext',)}) def test_network_association_list(self): bgpvpn = self.bgpvpns.first() na = {'network_associations': self.api_network_associations.list()} uri = BGPVPN_NETWORK_ASSOCIATION_PATH % bgpvpn.id neutronclient.list_ext('network_associations', uri, True).AndReturn(na) self.mox.ReplayAll() ret_val = api.bgpvpn.network_association_list(self.request, bgpvpn.id) for n in ret_val: self.assertIsInstance(n, api.bgpvpn.NetworkAssociation) @test.create_stubs({neutronclient: ('create_ext',)}) def test_network_association_create(self): bgpvpn = self.bgpvpns.first() network = self.networks.first() data = {'network_id': network.id} ret_dict = {'network_association': data} uri = BGPVPN_NETWORK_ASSOCIATION_PATH % bgpvpn.id neutronclient.create_ext(uri, ret_dict).AndReturn(ret_dict) self.mox.ReplayAll() ret_val = api.bgpvpn.network_association_create( self.request, bgpvpn.id, **data) self.assertIsInstance(ret_val, api.bgpvpn.NetworkAssociation) @test.create_stubs({neutronclient: ('delete_ext',)}) def test_network_association_delete(self): bgpvpn = self.bgpvpns.first() na = self.network_associations.first() api_bgpvpn = { 'network_association': self.api_network_associations.first()} uri_pattern = BGPVPN_NETWORK_ASSOCIATION_PATH uri = uri_pattern % bgpvpn.id + "/%s" neutronclient.delete_ext(uri, na.id).AndReturn(api_bgpvpn) self.mox.ReplayAll() api.bgpvpn.network_association_delete(self.request, na.id, bgpvpn.id) @test.create_stubs({neutronclient: ('list_ext',)}) def test_router_association_list(self): bgpvpn = self.bgpvpns.first() ra = {'router_associations': self.api_router_associations.list()} uri = BGPVPN_ROUTER_ASSOCIATION_PATH % bgpvpn.id neutronclient.list_ext('router_associations', uri, True).AndReturn(ra) self.mox.ReplayAll() ret_val = api.bgpvpn.router_association_list(self.request, bgpvpn.id) for n in ret_val: self.assertIsInstance(n, api.bgpvpn.RouterAssociation) @test.create_stubs({neutronclient: ('create_ext',)}) def test_router_association_create(self): bgpvpn = self.bgpvpns.first() router = self.routers.first() data = {'router_id': router.id} ret_dict = {'router_association': data} uri = BGPVPN_ROUTER_ASSOCIATION_PATH % bgpvpn.id neutronclient.create_ext(uri, ret_dict).AndReturn(ret_dict) self.mox.ReplayAll() ret_val = api.bgpvpn.router_association_create( self.request, bgpvpn.id, **data) self.assertIsInstance(ret_val, api.bgpvpn.RouterAssociation) @test.create_stubs({neutronclient: ('delete_ext',)}) def test_router_association_delete(self): bgpvpn = self.bgpvpns.first() ra = self.router_associations.first() api_bgpvpn = { 'router_association': self.api_router_associations.first() } uri_pattern = BGPVPN_ROUTER_ASSOCIATION_PATH uri = uri_pattern % bgpvpn.id + "/%s" neutronclient.delete_ext(uri, ra.id).AndReturn(api_bgpvpn) self.mox.ReplayAll() api.bgpvpn.router_association_delete(self.request, ra.id, bgpvpn.id) networking-bgpvpn-8.0.0/bgpvpn_dashboard/test/api_tests/__init__.py0000777000175100017510000000000013245511235025606 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/__init__.py0000777000175100017510000000000013245511235022634 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/locale/0000775000175100017510000000000013245511747021777 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/locale/fr/0000775000175100017510000000000013245511747022406 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/locale/fr/LC_MESSAGES/0000775000175100017510000000000013245511747024173 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/locale/fr/LC_MESSAGES/django.po0000666000175100017510000001376213245511271026000 0ustar zuulzuul00000000000000# Cédric Savignan , 2017. #zanata # Thomas Morin , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-12-19 20:14+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-12-11 09:48+0000\n" "Last-Translator: Thomas Morin \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "" "A single BGP Route Target or a comma-separated list of BGP Route Target. " "Example: 64512:1 or 64512:1,64512:2,64512:3" msgstr "" "Un simple Route Target BGP ou une liste de Route Targets BGP séparés par " "des virgules. Exemple: 64512:1 ou 64512:1,64512:2,64512:3 ." msgid "All Networks" msgstr "Tous les réseaux" msgid "All Routers" msgstr "Tous les routeurs" msgid "Associations type is not supported" msgstr "Type d'associations non supportés" msgid "BGP VPNs" msgstr "BGP VPNs" msgid "BGPVPN" msgstr "BGPVPN" #, python-format msgid "BGPVPN %s was successfully created." msgstr "Le BGPVPN %s a été correctement créé." #, python-format msgid "BGPVPN %s was successfully updated." msgstr "Le BGPVPN %s a été correctement mis à jour." msgid "BGPVPN Interconnections" msgstr "Interconnexions BGPVPN" msgid "" "Bug, inconsistency between neutron-lib and networking-bgpvpn for RTRD regex" msgstr "" "Bug, incohérence entre neutron-lib et networking-bgpvpn pour la regex RTRD" msgid "Create BGPVPN" msgstr "Créer un BGPVPN" msgid "Delete BGPVPN" msgid_plural "Delete BGPVPNs" msgstr[0] "Supprimer le BGPVPN" msgstr[1] "Supprimer les BGPVPNs" msgid "Deleted BGPVPN" msgid_plural "Deleted BGPVPNs" msgstr[0] "BGPVPN supprimé" msgstr[1] "BGPVPN supprimés" msgid "Edit BGPVPN" msgstr "Modifier le BGPVPN" msgid "Edit BGPVPN associations" msgstr "Modifier les associations de BGPVPN" msgid "Export Targets" msgstr "Export Targets" msgid "Export targets" msgstr "Export Targets" msgid "Export targets is not valid" msgstr "Export Targets invalide" #, python-format msgid "Failed to create BGPVPN %s" msgstr "Echec dans la création du BGPVPN %s" #, python-format msgid "Failed to delete BGPVPN %s" msgstr "Echec lors de la suppression du BGPVPN %s" #, python-format msgid "Failed to update BGPVPN %s" msgstr "Echec lors de la mise à jour du BGPVPN %s" msgid "ID" msgstr "ID" msgid "Import Targets" msgstr "Import Targets" msgid "Import targets" msgstr "Import Targets" msgid "Import targets is not valid" msgstr "Import Targets invalide" #, python-format msgid "Modified BGPVPN associations \"%s\"." msgstr "Associations BGPVPN modifiées \"%s\"." msgid "Name" msgstr "Nom" msgid "Network Associations" msgstr "Associations de Réseaux" msgid "Networks" msgstr "Réseaux" msgid "No network found." msgstr "Aucun réseau trouvé." msgid "No network selected." msgstr "Aucun réseau sélectionné." msgid "No router found." msgstr "Aucun routeur trouvé." msgid "No router selected." msgstr "Aucun routeur sélectionné." msgid "Project" msgstr "Projet" #, python-format msgid "Resource type not supported: %s" msgstr "Type de ressource non supporté: %s" msgid "Route Targets" msgstr "Route Targets" msgid "Route targets" msgstr "Route Targets" msgid "Route targets is not valid" msgstr "Route Targets invalide" msgid "Router Associations" msgstr "Associations de Routeurs" msgid "Routers" msgstr "Routeurs" msgid "Save" msgstr "Sauvegarder" msgid "Select a project" msgstr "Sélectionner un projet" msgid "Select the networks to be associated." msgstr "Sélectionner les réseaux à associer." msgid "Select the routers to be associated." msgstr "Sélectionner les routeurs à associer." msgid "Selected Networks" msgstr "Réseaux sélectionnés" msgid "Selected Routers" msgstr "Routeurs sélectionnés" msgid "Something went wrong when retrieving the list of associations" msgstr "Il y a eu un problème pour récupérer la liste des associations" #, python-format msgid "Something went wrong with BGPVPN %s" msgstr "Il y a eu un problème avec le BGPVPN %s" msgid "The type of VPN and the technology behind it." msgstr "Le type de VPN." msgid "Type" msgstr "Type" msgid "Unable to associate network {}: {}" msgstr "Impossible d''associer le réseau {}: {}" #, python-format msgid "Unable to associate resource %s" msgstr "Impossible d'associer la ressource %s" msgid "Unable to associate router {}: {}" msgstr "Impossible d''associer le routeur {}: {}" msgid "Unable to disassociate network {}" msgstr "Impossible de dissocier le réseau {}" msgid "Unable to disassociate router {}" msgstr "Impossible de dissocier le routeur {}" #, python-format msgid "Unable to modify BGPVPN associations \"%s\"." msgstr "Impossible de modifier les associations du BGPVPN \"%s\"." msgid "Unable to retrieve BGPVPN details." msgstr "Impossible de récupérer les détails du BGPVPN." #, python-format msgid "Unable to retrieve information about the tenant %s" msgstr "Impossible de récupérer les informations à propos du tenant %s" msgid "Unable to retrieve list of networks. Please try again later." msgstr "" "Impossible de récupérer la liste des réseaux. Veuillez réessayez plus tard." msgid "Unable to retrieve list of routers. Please try again later." msgstr "" "Impossible de récupérer la liste des routeurs. Veuillez réessayez plus tard." msgid "Unable to retrieve network associations" msgstr "Impossible de récupérer les associations de réseaux." msgid "Unable to retrieve router associations" msgstr "Impossible de récupérer les associations de routeurs" #, python-format msgid "Unsupported action type: %s" msgstr "Type d'action non supporté: %s" msgid "Update Change" msgstr "Sauvegarger les changements" msgid "Update Network Associations" msgstr "Modifier les Associations Réseaux" msgid "Update Router Associations" msgstr "Modifier les Association Routeurs" msgid "l2" msgstr "l2" msgid "l3" msgstr "l3" networking-bgpvpn-8.0.0/bgpvpn_dashboard/etc/0000775000175100017510000000000013245511747021313 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/bgpvpn_dashboard/etc/bgpvpn-horizon.conf0000666000175100017510000000370413245511235025142 0ustar zuulzuul00000000000000{ "context_is_admin": "role:admin", "admin_only": "rule:context_is_admin", "admin_or_owner": "rule:context_is_admin or tenant_id:%(tenant_id)s", "create_bgpvpn": "rule:admin_only", "get_bgpvpn": "rule:admin_or_owner", "get_bgpvpn:tenant_id": "rule:admin_only", "get_bgpvpn:route_targets": "rule:admin_only", "get_bgpvpn:import_targets": "rule:admin_only", "get_bgpvpn:export_targets": "rule:admin_only", "get_bgpvpn:route_distinguishers": "rule:admin_only", "update_bgpvpn": "rule:admin_or_owner", "update_bgpvpn:tenant_id": "rule:admin_only", "update_bgpvpn:route_targets": "rule:admin_only", "update_bgpvpn:import_targets": "rule:admin_only", "update_bgpvpn:export_targets": "rule:admin_only", "update_bgpvpn:route_distinguishers": "rule:admin_only", "delete_bgpvpn": "rule:admin_only", "create_bgpvpn_network_association": "rule:admin_or_owner", "get_bgpvpn_network_association": "rule:admin_or_owner", "get_bgpvpn_network_association:tenant_id": "rule:admin_only", "get_bgpvpn_network_associations": "rule:admin_or_owner", "update_bgpvpn_network_association": "rule:admin_or_owner", "delete_bgpvpn_network_association": "rule:admin_or_owner", "create_bgpvpn_router_association": "rule:admin_or_owner", "get_bgpvpn_router_association": "rule:admin_or_owner", "get_bgpvpn_router_association:tenant_id": "rule:admin_only", "get_bgpvpn_router_associations": "rule:admin_or_owner", "update_bgpvpn_router_association": "rule:admin_or_owner", "delete_bgpvpn_router_association": "rule:admin_or_owner", "create_bgpvpn_port_association": "rule:admin_or_owner", "get_bgpvpn_port_association": "rule:admin_or_owner", "get_bgpvpn_port_association:tenant_id": "rule:admin_only", "get_bgpvpn_port_associations": "rule:admin_or_owner", "update_bgpvpn_port_association": "rule:admin_or_owner", "delete_bgpvpn_port_association": "rule:admin_or_owner" } networking-bgpvpn-8.0.0/tox.ini0000666000175100017510000000623213245511235016545 0ustar zuulzuul00000000000000[tox] minversion = 1.6 envlist = py35,py27,pypy,pep8 skipsdist = True [testenv] usedevelop = True install_command = {toxinidir}/tools/tox_install.sh {env:UPPER_CONSTRAINTS_FILE:https://git.openstack.org/cgit/openstack/requirements/plain/upper-constraints.txt} {opts} {packages} setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = {toxinidir}/tools/ostestr_compat_shim.sh {posargs} python {toxinidir}/tools/django-manage.py test bgpvpn_dashboard.test.api_tests.bgpvpn_tests [testenv:releasenotes] commands = sphinx-build -W -a -E -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pep8] deps = {[testenv]deps} commands = flake8 flake8 doc/source/samples pylint --version pylint --rcfile=.pylintrc --output-format=colorized {posargs:networking_bgpvpn} pylint --rcfile=.pylintrc --output-format=colorized doc/source/samples neutron-db-manage --subproject networking-bgpvpn --database-connection sqlite:// check_migration {[testenv:genconfig]commands} [testenv:dsvm] setenv = OS_FAIL_ON_MISSING_DEPS=1 OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs} [testenv:functional] setenv = {[testenv]setenv} OS_TEST_TIMEOUT=180 OS_TEST_PATH=./networking_bgpvpn/tests/functional OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs} deps = {[testenv]deps} -r{toxinidir}/networking_bgpvpn/tests/functional/requirements.txt commands = {toxinidir}/tools/ostestr_compat_shim.sh {posargs} [testenv:dsvm-functional] basepython = python2.7 setenv = {[testenv:functional]setenv} {[testenv:dsvm]setenv} sitepackages=True deps = {[testenv:functional]deps} commands = {toxinidir}/tools/ostestr_compat_shim.sh {posargs} # If you are running the tests locally you should set the env variable # TEMPEST_CONFIG_DIR=/opt/stack/tempest/etc [testenv:api] sitepackages = True passenv = TEMPEST_CONFIG_DIR setenv = OS_TEST_PATH={toxinidir}/networking_bgpvpn_tempest/tests/api OS_TESTR_CONCURRENCY=1 # If you are running the tests locally you should set the env variable # TEMPEST_CONFIG_DIR=/opt/stack/tempest/etc [testenv:scenario] sitepackages = True passenv = TEMPEST_CONFIG_DIR setenv = OS_TEST_PATH={toxinidir}/networking_bgpvpn_tempest/tests/scenario OS_TESTR_CONCURRENCY=1 [testenv:py27] setenv = OS_FAIL_ON_MISSING_DEPS=1 [testenv:venv] commands = {posargs} [testenv:cover] basepython = python2.7 commands = python setup.py test --coverage --coverage-package-name=networking_bgpvpn --omit="*/tests/*" --testr-args='{posargs}' coverage report --omit="*/tests/*" -m [testenv:docs] commands = python setup.py build_sphinx [testenv:debug] commands = oslo_debug_helper -t networking_bgpvpn/tests/unit {posargs} [testenv:genconfig] commands = {toxinidir}/tools/generate_config_file_samples.sh [flake8] show-source = True # E123, E125 skipped as they are invalid PEP-8. # N530 direct neutron imports not allowed ignore = E123,E125,N530 builtins = _ exclude=.venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,.tmp [hacking] local-check-factory = neutron_lib.hacking.checks.factory networking-bgpvpn-8.0.0/.mailmap0000666000175100017510000000013113245511235016643 0ustar zuulzuul00000000000000# Format is: # # networking-bgpvpn-8.0.0/tools/0000775000175100017510000000000013245511747016375 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/tools/django-manage.py0000777000175100017510000000150013245511235021430 0ustar zuulzuul00000000000000#!/usr/bin/env python # 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 from django.core.management import execute_from_command_line if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bgpvpn_dashboard.test.settings") execute_from_command_line(sys.argv) networking-bgpvpn-8.0.0/tools/test-setup.sh0000777000175100017510000000370613245511235021051 0ustar zuulzuul00000000000000#!/bin/bash -xe # This script will be run by OpenStack CI before unit tests are run, # it sets up the test system as needed. # Developers should setup their test systems in a similar way. # This setup needs to be run as a user that can run sudo. # The root password for the MySQL database; pass it in via # MYSQL_ROOT_PW. DB_ROOT_PW=${MYSQL_ROOT_PW:-insecure_slave} # This user and its password are used by the tests, if you change it, # your tests might fail. DB_USER=openstack_citest DB_PW=openstack_citest sudo -H mysqladmin -u root password $DB_ROOT_PW # It's best practice to remove anonymous users from the database. If # a anonymous user exists, then it matches first for connections and # other connections from that host will not work. sudo -H mysql -u root -p$DB_ROOT_PW -h localhost -e " DELETE FROM mysql.user WHERE User=''; FLUSH PRIVILEGES; GRANT ALL PRIVILEGES ON *.* TO '$DB_USER'@'%' identified by '$DB_PW' WITH GRANT OPTION;" # Now create our database. mysql -u $DB_USER -p$DB_PW -h 127.0.0.1 -e " SET default_storage_engine=MYISAM; DROP DATABASE IF EXISTS openstack_citest; CREATE DATABASE openstack_citest CHARACTER SET utf8;" # Same for PostgreSQL # The root password for the PostgreSQL database; pass it in via # POSTGRES_ROOT_PW. DB_ROOT_PW=${POSTGRES_ROOT_PW:-insecure_slave} # Setup user root_roles=$(sudo -H -u postgres psql -t -c " SELECT 'HERE' from pg_roles where rolname='$DB_USER'") if [[ ${root_roles} == *HERE ]];then sudo -H -u postgres psql -c "ALTER ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" else sudo -H -u postgres psql -c "CREATE ROLE $DB_USER WITH SUPERUSER LOGIN PASSWORD '$DB_PW'" fi # Store password for tests cat << EOF > $HOME/.pgpass *:*:*:$DB_USER:$DB_PW EOF chmod 0600 $HOME/.pgpass # Now create our database psql -h 127.0.0.1 -U $DB_USER -d template1 -c "DROP DATABASE IF EXISTS openstack_citest" createdb -h 127.0.0.1 -U $DB_USER -l C -T template0 -E utf8 openstack_citest networking-bgpvpn-8.0.0/tools/tox_install.sh0000777000175100017510000000402113245511235021263 0ustar zuulzuul00000000000000#!/usr/bin/env bash # Many of neutron's repos suffer from the problem of depending on neutron, # but it not existing on pypi. # This wrapper for tox's package installer will use the existing package # if it exists, else use zuul-cloner if that program exists, else grab it # from neutron master via a hard-coded URL. That last case should only # happen with devs running unit tests locally. # From the tox.ini config page: # install_command=ARGV # default: # pip install {opts} {packages} ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner BRANCH_NAME=master GIT_BASE=${GIT_BASE:-https://git.openstack.org/} install_project() { local project=$1 local branch=${2:-$BRANCH_NAME} local module_name=${project//-/_} set +e project_installed=$(echo "import $module_name" | python 2>/dev/null ; echo $?) set -e if [ $project_installed -eq 0 ]; then echo "ALREADY INSTALLED" > /tmp/tox_install.txt echo "$project already installed; using existing package" elif [ -x "$ZUUL_CLONER" ]; then echo "ZUUL CLONER" > /tmp/tox_install.txt # Make this relative to current working directory so that # git clean can remove it. We cannot remove the directory directly # since it is referenced after $install_cmd -e mkdir -p .tmp PROJECT_DIR=$(/bin/mktemp -d -p $(pwd)/.tmp) pushd $PROJECT_DIR $ZUUL_CLONER --cache-dir \ /opt/git \ --branch $branch \ http://git.openstack.org \ openstack/$project cd openstack/$project $install_cmd -e . popd else echo "PIP HARDCODE" > /tmp/tox_install.txt local GIT_REPO="$GIT_BASE/openstack/$project" SRC_DIR="$VIRTUAL_ENV/src/$project" git clone --depth 1 --branch $branch $GIT_REPO $SRC_DIR $install_cmd -U -e $SRC_DIR fi } set -e install_cmd="pip install -c$1" shift install_project neutron install_project horizon install_project networking-bagpipe install_project networking-odl 4.0.0 $install_cmd -U $* exit $? networking-bgpvpn-8.0.0/tools/generate_config_file_samples.sh0000777000175100017510000000144013245511235024567 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 networking-bgpvpn-8.0.0/tools/ostestr_compat_shim.sh0000777000175100017510000000025113245511235023012 0ustar zuulzuul00000000000000#!/bin/sh # preserve old behavior of using an arg as a regex when '--' is not present case $@ in (*--*) ostestr $@;; ('') ostestr;; (*) ostestr --regex "$@" esac networking-bgpvpn-8.0.0/.pylintrc0000666000175100017510000000553713245511235017106 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) no-member, no-method-argument, no-self-argument, # "W" Warnings for stylistic problems or minor programming issues abstract-method, arguments-differ, attribute-defined-outside-init, bad-builtin, bad-indentation, broad-except, dangerous-default-value, deprecated-lambda, expression-not-assigned, fixme, global-statement, no-init, non-parent-init-called, not-callable, protected-access, redefined-builtin, redefined-outer-name, star-args, super-init-not-called, super-on-old-class, unpacking-non-sequence, unused-argument, # "C" Coding convention violations bad-continuation, invalid-name, missing-docstring, 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= [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 networking-bgpvpn-8.0.0/networking_bgpvpn_heat/0000775000175100017510000000000013245511747022001 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/0000775000175100017510000000000013245511747023617 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01bis_router-tenant.yaml0000666000175100017510000000145113245511235032634 0ustar zuulzuul00000000000000description: BGPVPN networking example (tenant) heat_template_version: '2013-05-23' parameters: bgpvpn: type: string description: id of BGPVPN to bind the network to resources: Net1: type: OS::Neutron::Net SubNet1: type: OS::Neutron::Subnet properties: network: { get_resource: Net1 } cidr: 192.168.10.0/24 Router1: type: OS::Neutron::Router router_interface1: type: OS::Neutron::RouterInterface properties: router_id: { get_resource: Router1 } subnet_id: { get_resource: SubNet1 } BGPVPN_router_assoc1: type: OS::Neutron::BGPVPN-ROUTER-ASSOCIATION properties: bgpvpn_id: { get_param: bgpvpn } router_id: { get_resource: Router1 } networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01-admin.yaml0000666000175100017510000000044713245511235030341 0ustar zuulzuul00000000000000description: BGPVPN networking example (admin) heat_template_version: '2013-05-23' resources: BGPVPN1: type: OS::Neutron::BGPVPN properties: import_targets: [ "100:1001"] export_targets: [ "100:1002"] route_targets: [ "100:1000" ] name: "default_vpn" networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-04-tenant.yaml0000666000175100017510000000204413245511235030540 0ustar zuulzuul00000000000000description: BGPVPN networking example (tenant) heat_template_version: '2013-05-23' resources: Net1: type: OS::Neutron::Net SubNet1: type: OS::Neutron::Subnet properties: network: { get_resource: Net1 } cidr: 192.168.10.0/24 BGPVPN_NET_assoc1: type: OS::Neutron::BGPVPN-NET-ASSOCIATION properties: bgpvpn_id: "default_vpn" network_id: { get_resource: Net1 } Net2: type: OS::Neutron::Net SubNet2: type: OS::Neutron::Subnet properties: network: { get_resource: Net2 } cidr: 192.168.10.0/24 Router: type: OS::Neutron::Router router_interface: type: OS::Neutron::RouterInterface properties: router_id: { get_resource: Router } subnet_id: { get_resource: SubNet2 } BGPVPN_router_assoc1: type: OS::Neutron::BGPVPN-ROUTER-ASSOCIATION properties: bgpvpn_id: "default_vpn" router_id: { get_resource: Router } networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01-tenant.yaml0000666000175100017510000000107213245511235030535 0ustar zuulzuul00000000000000description: BGPVPN networking example (tenant) heat_template_version: '2013-05-23' parameters: bgpvpn: type: string description: id of BGPVPN to bind the network to resources: Net1: type: OS::Neutron::Net SubNet1: type: OS::Neutron::Subnet properties: network: { get_resource: Net1 } cidr: 192.168.10.0/24 BGPVPN_NET_assoc1: type: OS::Neutron::BGPVPN-NET-ASSOCIATION properties: bgpvpn_id: { get_param: bgpvpn } network_id: { get_resource: Net1 } networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-00.yaml0000666000175100017510000000116713245511235027252 0ustar zuulzuul00000000000000description: BGPVPN networking example (admin) heat_template_version: '2013-05-23' resources: BGPVPN1: type: OS::Neutron::BGPVPN properties: import_targets: [ "100:1001"] export_targets: [ "100:1002"] route_targets: [ "100:1000" ] name: "default VPN" Net1: type: OS::Neutron::Net SubNet1: type: OS::Neutron::Subnet properties: network: { get_resource: Net1 } cidr: 192.168.10.0/24 BGPVPN_NET_assoc1: type: OS::Neutron::BGPVPN-NET-ASSOCIATION properties: bgpvpn_id: { get_resource: BGPVPN1 } network_id: { get_resource: Net1 } networking-bgpvpn-8.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-04-admin.yaml0000666000175100017510000000050213245511235030334 0ustar zuulzuul00000000000000description: BGPVPN networking example (admin) heat_template_version: '2013-05-23' resources: BGPVPN1: type: OS::Neutron::BGPVPN properties: import_targets: [ "100:1001"] export_targets: [ "100:1002"] route_targets: [ "100:1000" ] name: "default_vpn" tenant_id: "demo" networking-bgpvpn-8.0.0/networking_bgpvpn_heat/__init__.py0000666000175100017510000000000013245511235024072 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_heat/_i18n.py0000666000175100017510000000211113245511235023256 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 oslo_i18n DOMAIN = "networking_bgpvpn_heat" _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" # requires oslo.i18n >=2.1.0 _C = _translators.contextual_form # The plural translation function using the name "_P" # requires oslo.i18n >=2.1.0 _P = _translators.plural_form def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) networking-bgpvpn-8.0.0/networking_bgpvpn_heat/bgpvpnservice.py0000666000175100017510000002303213245511235025222 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 heat.common import exception from heat.engine import attributes from heat.engine.clients.os.neutron import neutron_constraints from heat.engine import constraints from heat.engine import properties from heat.engine.resources.openstack.neutron import neutron from networking_bgpvpn_heat._i18n import _ class BGPVPN(neutron.NeutronResource): """A resource for BGPVPN service in neutron. """ PROPERTIES = ( NAME, TYPE, DESCRIPTION, ROUTE_DISTINGUISHERS, IMPORT_TARGETS, EXPORT_TARGETS, ROUTE_TARGETS, TENANT_ID ) = ( 'name', 'type', 'description', 'route_distinguishers', 'import_targets', 'export_targets', 'route_targets', 'tenant_id' ) ATTRIBUTES = ( SHOW, STATUS ) = ( 'show', 'status' ) properties_schema = { NAME: properties.Schema( properties.Schema.STRING, _('Name for the bgpvpn.'), ), TYPE: properties.Schema( properties.Schema.STRING, _('BGP VPN type selection between L3VPN (l3) and ' 'EVPN (l2), default:l3'), required=False, default='l3', constraints=[ constraints.AllowedValues(['l2', 'l3']) ] ), DESCRIPTION: properties.Schema( properties.Schema.STRING, _('Description for the bgpvpn.'), required=False, ), TENANT_ID: properties.Schema( properties.Schema.STRING, _('Tenant this bgpvpn belongs to (name or id).'), required=False, constraints=[ constraints.CustomConstraint('keystone.project') ] ), ROUTE_DISTINGUISHERS: properties.Schema( properties.Schema.LIST, _('List of RDs that will be used to advertize BGPVPN routes.'), required=False, # TODO(tmorin): add a pattern constraint ), IMPORT_TARGETS: properties.Schema( properties.Schema.LIST, _('List of additional Route Targets to import from.'), required=False, # TODO(tmorin): add a pattern constraint ), EXPORT_TARGETS: properties.Schema( properties.Schema.LIST, _('List of additional Route Targets to export to.'), required=False, # TODO(tmorin): add a pattern constraint ), ROUTE_TARGETS: properties.Schema( properties.Schema.LIST, _('Route Targets list to import/export for this BGPVPN.'), required=False, # TODO(tmorin): add a pattern constraint ), } attributes_schema = { STATUS: attributes.Schema( _('Status of bgpvpn.'), ), SHOW: attributes.Schema( _('All attributes.') ), } def validate(self): super(BGPVPN, self).validate() def handle_create(self): props = self.prepare_properties( self.properties, self.physical_resource_name()) if 'tenant_id' in props: tenant_id = self.client_plugin('keystone').get_project_id( props['tenant_id']) props['tenant_id'] = tenant_id bgpvpn = self.neutron().create_bgpvpn({'bgpvpn': props}) self.resource_id_set(bgpvpn['bgpvpn']['id']) def handle_update(self, json_snippet, tmpl_diff, prop_diff): raise NotImplementedError() def handle_delete(self): try: self.neutron().delete_bgpvpn(self.resource_id) except Exception as ex: self.client_plugin().ignore_not_found(ex) else: return True def _confirm_delete(self): while True: try: yield self._show_resource() except exception.NotFound: return def _show_resource(self): return self.neutron().show_bgpvpn(self.resource_id) # this class is registered to Heat via a setuptools entry point # (see setup.cfg) class BGPVPNConstraint(neutron_constraints.NeutronConstraint): resource_name = 'bgpvpn' class BGPVPNNetAssoc(neutron.NeutronResource): """A resource for BGPVPNNetAssoc in neutron. """ PROPERTIES = ( BGPVPN_ID, NETWORK_ID ) = ( 'bgpvpn_id', 'network_id' ) ATTRIBUTES = ( SHOW, STATUS ) = ( 'show', 'status' ) properties_schema = { BGPVPN_ID: properties.Schema( properties.Schema.STRING, _('name or ID of the BGPVPN.'), required=True, constraints=[ constraints.CustomConstraint('neutron.bgpvpn') ] ), NETWORK_ID: properties.Schema( properties.Schema.STRING, _('Network which shall be associated with the BGPVPN.'), required=True, constraints=[ constraints.CustomConstraint('neutron.network') ] ) } attributes_schema = { STATUS: attributes.Schema( _('Status of bgpvpn.'), ), SHOW: attributes.Schema( _('All attributes.') ), } def validate(self): super(BGPVPNNetAssoc, self).validate() def handle_create(self): self.props = self.prepare_properties(self.properties, self.physical_resource_name()) body = self.props.copy() body.pop('bgpvpn_id') bgpvpn_id = self.client_plugin().find_resourceid_by_name_or_id( 'bgpvpn', self.props['bgpvpn_id']) net_assoc = self.neutron().create_network_association( bgpvpn_id, {'network_association': body}) self.resource_id_set(net_assoc['network_association']['id']) def handle_update(self, json_snippet, tmpl_diff, prop_diff): raise NotImplementedError() def handle_delete(self): try: self.neutron().delete_network_association( self.resource_id, self.properties['bgpvpn_id']) except Exception as ex: self.client_plugin().ignore_not_found(ex) else: return True def _confirm_delete(self): while True: try: self._show_resource() except exception.NotFound: return def _show_resource(self): return self.neutron().show_network_association( self.resource_id, self.properties['bgpvpn_id']) class BGPVPNRouterAssoc(neutron.NeutronResource): """A resource for BGPVPNRouterAssoc in neutron. """ PROPERTIES = ( BGPVPN_ID, ROUTER_ID ) = ( 'bgpvpn_id', 'router_id' ) ATTRIBUTES = ( SHOW, STATUS ) = ( 'show', 'status' ) properties_schema = { BGPVPN_ID: properties.Schema( properties.Schema.STRING, _('name or ID of the BGPVPN.'), required=True, constraints=[ constraints.CustomConstraint('neutron.bgpvpn') ] ), ROUTER_ID: properties.Schema( properties.Schema.STRING, _('Router which shall be associated with the BGPVPN.'), required=True, constraints=[ constraints.CustomConstraint('neutron.router') ] ) } attributes_schema = { STATUS: attributes.Schema( _('Status of bgpvpn.'), ), SHOW: attributes.Schema( _('All attributes.') ), } def validate(self): super(BGPVPNRouterAssoc, self).validate() def handle_create(self): self.props = self.prepare_properties(self.properties, self.physical_resource_name()) body = self.props.copy() body.pop('bgpvpn_id') bgpvpn_id = self.client_plugin().find_resourceid_by_name_or_id( 'bgpvpn', self.props['bgpvpn_id']) router_assoc = self.neutron().create_router_association( bgpvpn_id, {'router_association': body}) self.resource_id_set(router_assoc['router_association']['id']) def handle_update(self, json_snippet, tmpl_diff, prop_diff): raise NotImplementedError() def handle_delete(self): try: self.neutron().delete_router_association( self.resource_id, self.properties['bgpvpn_id']) except Exception as ex: self.client_plugin().ignore_not_found(ex) else: return True def _confirm_delete(self): while True: try: self._show_resource() except exception.NotFound: return def _show_resource(self): return self.neutron().show_router_association( self.resource_id, self.properties['bgpvpn_id']) def resource_mapping(): return { 'OS::Neutron::BGPVPN': BGPVPN, 'OS::Neutron::BGPVPN-NET-ASSOCIATION': BGPVPNNetAssoc, 'OS::Neutron::BGPVPN-ROUTER-ASSOCIATION': BGPVPNRouterAssoc, } networking-bgpvpn-8.0.0/networking_bgpvpn_heat/locale/0000775000175100017510000000000013245511747023240 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_heat/locale/en_GB/0000775000175100017510000000000013245511747024212 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_heat/locale/en_GB/LC_MESSAGES/0000775000175100017510000000000013245511747025777 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn_heat/locale/en_GB/LC_MESSAGES/networking_bgpvpn_heat.po0000666000175100017510000000351113245511235033075 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2017-12-19 20:13+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2017-12-12 09:18+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en-GB\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "All attributes." msgstr "All attributes." msgid "BGP VPN type selection between L3VPN (l3) and EVPN (l2), default:l3" msgstr "BGP VPN type selection between L3VPN (l3) and EVPN (l2), default:l3" msgid "Description for the bgpvpn." msgstr "Description for the BGPVPN." msgid "List of RDs that will be used to advertize BGPVPN routes." msgstr "List of RDs that will be used to advertise BGPVPN routes." msgid "List of additional Route Targets to export to." msgstr "List of additional Route Targets to export to." msgid "List of additional Route Targets to import from." msgstr "List of additional Route Targets to import from." msgid "Name for the bgpvpn." msgstr "Name for the BGPVPN." msgid "Network which shall be associated with the BGPVPN." msgstr "Network which shall be associated with the BGPVPN." msgid "Route Targets list to import/export for this BGPVPN." msgstr "Route Targets list to import/export for this BGPVPN." msgid "Router which shall be associated with the BGPVPN." msgstr "Router which shall be associated with the BGPVPN." msgid "Status of bgpvpn." msgstr "Status of BGPVPN." msgid "Tenant this bgpvpn belongs to (name or id)." msgstr "Tenant this BGPVPN belongs to (name or id)." msgid "name or ID of the BGPVPN." msgstr "name or ID of the BGPVPN." networking-bgpvpn-8.0.0/AUTHORS0000664000175100017510000000606113245511745016306 0ustar zuulzuul00000000000000Akihiro Motoki Andreas Jaeger Anh Tran Antoine Eiche Armando Migliaccio Atsushi SAKAI Bin-Lu <369283883@qq.com> Bob Melander Boden R Cao Xuan Hoang Cedric Savignan Claire Delcourt Cédric Savignan Daniel Mellado Dariusz Smigiel Davanum Srinivas David Blaisonneau Deepthi V V Doug Hellmann Edouard Thuleau Emilien Macchi Flavio Percoco Francois Eleouet Georg Kunz Henry Henry Gessau Henry Gessau Ihar Hrachyshka James E. Blair Jean-Philippe Braun Jeremy Stanley Ji-Wei Joan Meseguer Jon Schlueter Ken'ichi Ohmichi Li-zhigang Luke Hinds Luong Anh Tuan Mathieu Rohon Monty Taylor Morgan Richomme Ngo Quoc Cuong Nguyen Hung Phuong Nguyen Van Trung Nikolas Hermanns Nishant Kumar Omar Sanhaji OpenStack Release Bot Paul Carver Peter V. Saveliev Pierre Crégut Ramanjaneya Romanos Skiadas Thomas Monguillon Thomas Morin Thomas Morin Vishal Thapar Vivekanandan Narasimhan Vu Cong Tuan Wim De Clercq YAMAMOTO Takashi Zuul bfernando bhargavaregalla chenxing davidblaisonneau-orange gengchc2 ghanshyam janonymous mathieu-rohon melissaml qinchunhua ravikiran suresh kumar zhangyanxian zhaojingjing0067370 Édouard Thuleau Édouard Thuleau Łukasz Rajewski networking-bgpvpn-8.0.0/README.rst0000666000175100017510000000252113245511235016716 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/networking-bgpvpn.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on =============================================== BGP-MPLS VPN Extension for OpenStack Networking =============================================== This project provides an API and Framework to interconnect BGP/MPLS VPNs to Openstack Neutron networks, routers and ports. The Border Gateway Protocol and Multi-Protocol Label Switching are widely used Wide Area Networking technologies. The primary purpose of this project is to allow attachment of Neutron networks and/or routers to VPNs built in carrier provided WANs using these standard protocols. An additional purpose of this project is to enable the use of these technologies within the Neutron networking environment. A vendor-neutral API and data model are provided such that multiple backends may be "plugged in" while offering the same tenant facing API. A reference implementation based on an Open Source BGP implementation is also provided. * Free software: Apache license * Source: http://git.openstack.org/cgit/openstack/networking-bgpvpn * Bugs: http://bugs.launchpad.net/bgpvpn * Doc: https://docs.openstack.org/networking-bgpvpn/latest/ networking-bgpvpn-8.0.0/.coveragerc0000666000175100017510000000021513245511235017346 0ustar zuulzuul00000000000000[run] branch = True source = networking-bgpvpn omit = networking-bgpvpn/tests/*,networking-bgpvpn/openstack/* [report] ignore_errors = True networking-bgpvpn-8.0.0/specs/0000775000175100017510000000000013245511747016352 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/specs/bgpvpn.rst0000666000175100017510000000016413245511235020373 0ustar zuulzuul00000000000000This is a work in progress to implement the specs currently discussed at: https://review.openstack.org/#/c/177740/ networking-bgpvpn-8.0.0/setup.cfg0000666000175100017510000000460613245511747017066 0ustar zuulzuul00000000000000[metadata] name = networking-bgpvpn summary = API and Framework to interconnect bgpvpn to neutron networks description-file = README.rst author = OpenStack author-email = openstack-dev@lists.openstack.org home-page = https://docs.openstack.org/networking-bgpvpn/latest/ 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 :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.5 [files] packages = networking_bgpvpn networking_bgpvpn_tempest networking_bgpvpn_heat bgpvpn_dashboard data_files = etc/neutron/policy.d = etc/neutron/policy.d/bgpvpn.conf etc/neutron = etc/neutron/networking_bgpvpn.conf [entry_points] neutronclient.extension = bgpvpn = networking_bgpvpn.neutronclient.neutron.v2_0.bgpvpn.bgpvpn neutron.db.alembic_migrations = networking-bgpvpn = networking_bgpvpn.neutron.db.migration:alembic_migrations tempest.test_plugins = networking-bgpvpn = networking_bgpvpn_tempest.plugin:NetworkingBgpvpnTempestPlugin heat.constraints = neutron.bgpvpn = networking_bgpvpn_heat.bgpvpnservice:BGPVPNConstraint neutron.service_plugins = bgpvpn = networking_bgpvpn.neutron.services.plugin:BGPVPNPlugin oslo.config.opts = networking-bgpvpn.service_provider = networking_bgpvpn.neutron.opts:list_service_provider networking-bgpvpn.opencontrail_driver = networking_bgpvpn.neutron.opts:list_opencontrail_driver_opts oslo.config.opts.defaults = networking-bgpvpn.service_provider = networking_bgpvpn.neutron.opts:set_service_provider_default [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 warning-is-error = 1 [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = networking_bgpvpn/locale domain = networking_bgpvpn [update_catalog] domain = networking-bgpvpn output_dir = networking_bgpvpn/locale input_file = networking_bgpvpn/locale/networking-bgpvpn.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = networking_bgpvpn/locale/networking-bgpvpn.pot [openstack_translations] django_modules = bgpvpn_dashboard python_modules = networking_bgpvpn networking_bgpvpn_heat [egg_info] tag_build = tag_date = 0 networking-bgpvpn-8.0.0/babel.cfg0000666000175100017510000000002113245511235016746 0ustar zuulzuul00000000000000[python: **.py] networking-bgpvpn-8.0.0/etc/0000775000175100017510000000000013245511747016010 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/etc/neutron/0000775000175100017510000000000013245511747017502 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/etc/neutron/policy.d/0000775000175100017510000000000013245511747021223 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/etc/neutron/policy.d/bgpvpn.conf0000666000175100017510000000376213245511235023370 0ustar zuulzuul00000000000000{ "admin_only": "rule:context_is_admin", "admin_or_owner": "rule:context_is_admin or tenant_id:%(tenant_id)s", "create_bgpvpn": "rule:admin_only", "get_bgpvpn": "rule:admin_or_owner", "get_bgpvpn:tenant_id": "rule:admin_only", "get_bgpvpn:route_targets": "rule:admin_only", "get_bgpvpn:import_targets": "rule:admin_only", "get_bgpvpn:export_targets": "rule:admin_only", "get_bgpvpn:route_distinguishers": "rule:admin_only", "get_bgpvpn:vni": "rule:admin_only", "update_bgpvpn": "rule:admin_or_owner", "update_bgpvpn:tenant_id": "rule:admin_only", "update_bgpvpn:route_targets": "rule:admin_only", "update_bgpvpn:import_targets": "rule:admin_only", "update_bgpvpn:export_targets": "rule:admin_only", "update_bgpvpn:route_distinguishers": "rule:admin_only", "update_bgpvpn:vni": "rule:admin_only", "delete_bgpvpn": "rule:admin_only", "create_bgpvpn_network_association": "rule:admin_or_owner", "get_bgpvpn_network_association": "rule:admin_or_owner", "get_bgpvpn_network_association:tenant_id": "rule:admin_only", "get_bgpvpn_network_associations": "rule:admin_or_owner", "update_bgpvpn_network_association": "rule:admin_or_owner", "delete_bgpvpn_network_association": "rule:admin_or_owner", "create_bgpvpn_router_association": "rule:admin_or_owner", "get_bgpvpn_router_association": "rule:admin_or_owner", "get_bgpvpn_router_association:tenant_id": "rule:admin_only", "get_bgpvpn_router_associations": "rule:admin_or_owner", "update_bgpvpn_router_association": "rule:admin_or_owner", "delete_bgpvpn_router_association": "rule:admin_or_owner", "create_bgpvpn_port_association": "rule:admin_or_owner", "get_bgpvpn_port_association": "rule:admin_or_owner", "get_bgpvpn_port_association:tenant_id": "rule:admin_only", "get_bgpvpn_port_associations": "rule:admin_or_owner", "update_bgpvpn_port_association": "rule:admin_or_owner", "delete_bgpvpn_port_association": "rule:admin_or_owner" } networking-bgpvpn-8.0.0/etc/neutron/networking_bgpvpn.conf0000666000175100017510000000046013245511235024106 0ustar zuulzuul00000000000000[service_providers] # both cannot be default, please choose: service_provider=BGPVPN:Dummy:networking_bgpvpn.neutron.services.service_drivers.driver_api.BGPVPNDriver:default #service_provider=BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe.BaGPipeBGPVPNDriver:default networking-bgpvpn-8.0.0/etc/oslo-config-generator/0000775000175100017510000000000013245511747022213 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/etc/oslo-config-generator/networking-bgpvpn.conf0000666000175100017510000000020213245511235026527 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/networking-bgpvpn.conf.sample wrap_width = 79 namespace = networking-bgpvpn.service_provider networking-bgpvpn-8.0.0/etc/oslo-config-generator/opencontrail-driver.conf0000666000175100017510000000020713245511235027041 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/opencontrail-driver.conf.sample wrap_width = 79 namespace = networking-bgpvpn.opencontrail_driver networking-bgpvpn-8.0.0/networking_bgpvpn/0000775000175100017510000000000013245511747021000 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/version.py0000666000175100017510000000127013245511235023031 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('networking-bgpvpn') networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/0000775000175100017510000000000013245511747022472 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/0000775000175100017510000000000013245511747023057 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/head.py0000666000175100017510000000134513245511235024327 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.db.migration.models import head # pylint: disable=unused-import import networking_bgpvpn.neutron.db.bgpvpn_db # noqa def get_metadata(): return head.model_base.BASEV2.metadata networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/__init__.py0000666000175100017510000000000013245511235025150 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/bgpvpn_db.py0000666000175100017510000006022613245511235025372 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_db import exception as db_exc from oslo_log import log from oslo_utils import uuidutils import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.orm import exc from neutron.db import _model_query as model_query from neutron.db import api as db_api from neutron.db import common_db_mixin from neutron_lib.api.definitions import bgpvpn as bgpvpn_def from neutron_lib.api.definitions import bgpvpn_routes_control as bgpvpn_rc_def from neutron_lib.api.definitions import bgpvpn_vni as bgpvpn_vni_def from neutron_lib.db import constants as db_const from neutron_lib.db import model_base from neutron_lib.plugins import directory from networking_bgpvpn.neutron.extensions import bgpvpn as bgpvpn_ext from networking_bgpvpn.neutron.extensions\ import bgpvpn_routes_control as bgpvpn_rc_ext from networking_bgpvpn.neutron.services.common import utils LOG = log.getLogger(__name__) class HasProjectNotNullable(model_base.HasProject): project_id = sa.Column(sa.String(db_const.PROJECT_ID_FIELD_SIZE), index=True, nullable=False) class BGPVPNNetAssociation(model_base.BASEV2, model_base.HasId, HasProjectNotNullable): """Represents the association between a bgpvpn and a network.""" __tablename__ = 'bgpvpn_network_associations' bgpvpn_id = sa.Column(sa.String(36), sa.ForeignKey('bgpvpns.id', ondelete='CASCADE'), nullable=False) network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id', ondelete='CASCADE'), nullable=False) sa.UniqueConstraint(bgpvpn_id, network_id) network = orm.relationship("Network", backref=orm.backref('bgpvpn_associations', cascade='all'), lazy='joined',) class BGPVPNRouterAssociation(model_base.BASEV2, model_base.HasId, HasProjectNotNullable): """Represents the association between a bgpvpn and a router.""" __tablename__ = 'bgpvpn_router_associations' bgpvpn_id = sa.Column(sa.String(36), sa.ForeignKey('bgpvpns.id', ondelete='CASCADE'), nullable=False) router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', ondelete='CASCADE'), nullable=False) sa.UniqueConstraint(bgpvpn_id, router_id) advertise_extra_routes = sa.Column(sa.Boolean(), nullable=False, server_default=sa.true()) router = orm.relationship("Router", backref=orm.backref('bgpvpn_associations', cascade='all'), lazy='joined',) class BGPVPNPortAssociation(model_base.BASEV2, model_base.HasId, HasProjectNotNullable): """Represents the association between a bgpvpn and a port.""" __tablename__ = 'bgpvpn_port_associations' bgpvpn_id = sa.Column(sa.String(36), sa.ForeignKey('bgpvpns.id', ondelete='CASCADE'), nullable=False) port_id = sa.Column(sa.String(36), sa.ForeignKey('ports.id', ondelete='CASCADE'), nullable=False) sa.UniqueConstraint(bgpvpn_id, port_id) advertise_fixed_ips = sa.Column(sa.Boolean(), nullable=False, server_default=sa.true()) port = orm.relationship("Port", backref=orm.backref('bgpvpn_associations', cascade='all'), lazy='joined',) routes = orm.relationship("BGPVPNPortAssociationRoute", backref="bgpvpn_port_associations") class BGPVPNPortAssociationRoute(model_base.BASEV2, model_base.HasId): """Represents an item of the 'routes' attribute of a port association.""" __tablename__ = 'bgpvpn_port_association_routes' port_association_id = sa.Column( sa.String(length=36), sa.ForeignKey('bgpvpn_port_associations.id', ondelete='CASCADE'), nullable=False) type = sa.Column(sa.Enum(*bgpvpn_rc_def.ROUTE_TYPES, name="bgpvpn_port_assoc_route_type"), nullable=False) local_pref = sa.Column(sa.BigInteger(), nullable=True) # prefix is NULL unless type is 'prefix' prefix = sa.Column(sa.String(49), nullable=True) # bgpvpn_id is NULL unless type is 'bgpvpn' bgpvpn_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgpvpns.id', ondelete='CASCADE'), nullable=True) port_association = orm.relationship( "BGPVPNPortAssociation", backref=orm.backref('port_association_routes', cascade='all'), lazy='joined') bgpvpn = orm.relationship( "BGPVPN", backref=orm.backref("port_association_routes", uselist=False, lazy='select', cascade='all, delete-orphan'), lazy='joined') class BGPVPN(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a BGPVPN Object.""" name = sa.Column(sa.String(255)) type = sa.Column(sa.Enum("l2", "l3", name="bgpvpn_type"), nullable=False) route_targets = sa.Column(sa.String(255), nullable=False) import_targets = sa.Column(sa.String(255), nullable=True) export_targets = sa.Column(sa.String(255), nullable=True) route_distinguishers = sa.Column(sa.String(255), nullable=True) vni = sa.Column(sa.Integer, nullable=True) local_pref = sa.Column(sa.BigInteger, nullable=True) network_associations = orm.relationship("BGPVPNNetAssociation", backref="bgpvpn", lazy='select', cascade='all, delete-orphan') router_associations = orm.relationship("BGPVPNRouterAssociation", backref="bgpvpn", lazy='select', cascade='all, delete-orphan') port_associations = orm.relationship("BGPVPNPortAssociation", backref="bgpvpn", lazy='select', cascade='all, delete-orphan') def _list_bgpvpns_result_filter_hook(query, filters): values = filters and filters.get('networks', []) if values: query = query.join(BGPVPNNetAssociation) query = query.filter(BGPVPNNetAssociation.network_id.in_(values)) values = filters and filters.get('routers', []) if values: query = query.join(BGPVPNRouterAssociation) query = query.filter(BGPVPNRouterAssociation.router_id.in_(values)) values = filters and filters.get('ports', []) if values: query = query.join(BGPVPNRouterAssociation) query = query.filter(BGPVPNPortAssociation.port_id.in_(values)) return query @db_api.context_manager.writer def _add_port_assoc_route_db_from_dict(context, route, port_association_id): if route['type'] == 'prefix': kwargs = {'prefix': route['prefix']} elif route['type'] == 'bgpvpn': kwargs = {'bgpvpn_id': route['bgpvpn_id']} else: # not reached pass context.session.add(BGPVPNPortAssociationRoute( port_association_id=port_association_id, type=route['type'], local_pref=route.get('local_pref', None), **kwargs )) def port_assoc_route_dict_from_db(route_db): route = { 'type': route_db.type, 'local_pref': route_db.local_pref } if route_db.type == 'prefix': route.update({'prefix': route_db.prefix}) elif route_db.type == 'bgpvpn': route.update({'bgpvpn_id': route_db.bgpvpn_id}) return route class BGPVPNPluginDb(common_db_mixin.CommonDbMixin): """BGPVPN service plugin database class using SQLAlchemy models.""" def __new__(cls, *args, **kwargs): model_query.register_hook( BGPVPN, "bgpvpn_filter_by_resource_association", query_hook=None, filter_hook=None, result_filters=_list_bgpvpns_result_filter_hook) return super(BGPVPNPluginDb, cls).__new__(cls, *args, **kwargs) @db_api.context_manager.reader def _get_bgpvpns_for_tenant(self, session, tenant_id, fields): try: qry = session.query(BGPVPN) bgpvpns = qry.filter_by(tenant_id=tenant_id) except exc.NoResultFound: return return [self._make_bgpvpn_dict(bgpvpn, fields=fields) for bgpvpn in bgpvpns] @db_api.context_manager.reader def _make_bgpvpn_dict(self, bgpvpn_db, fields=None): net_list = [net_assocs.network_id for net_assocs in bgpvpn_db.network_associations] router_list = [router_assocs.router_id for router_assocs in bgpvpn_db.router_associations] port_list = [port_assocs.port_id for port_assocs in bgpvpn_db.port_associations] res = { 'id': bgpvpn_db['id'], 'tenant_id': bgpvpn_db['tenant_id'], 'networks': net_list, 'routers': router_list, 'ports': port_list, 'name': bgpvpn_db['name'], 'type': bgpvpn_db['type'], 'route_targets': utils.rtrd_str2list(bgpvpn_db['route_targets']), 'route_distinguishers': utils.rtrd_str2list(bgpvpn_db['route_distinguishers']), 'import_targets': utils.rtrd_str2list(bgpvpn_db['import_targets']), 'export_targets': utils.rtrd_str2list(bgpvpn_db['export_targets']) } plugin = directory.get_plugin(bgpvpn_def.LABEL) if utils.is_extension_supported(plugin, bgpvpn_vni_def.ALIAS): res[bgpvpn_vni_def.VNI] = bgpvpn_db.get(bgpvpn_vni_def.VNI) if utils.is_extension_supported(plugin, bgpvpn_rc_def.ALIAS): res[bgpvpn_rc_def.LOCAL_PREF_KEY] = bgpvpn_db.get( bgpvpn_rc_def.LOCAL_PREF_KEY) return self._fields(res, fields) @db_api.context_manager.writer def create_bgpvpn(self, context, bgpvpn): rt = utils.rtrd_list2str(bgpvpn['route_targets']) i_rt = utils.rtrd_list2str(bgpvpn['import_targets']) e_rt = utils.rtrd_list2str(bgpvpn['export_targets']) rd = utils.rtrd_list2str(bgpvpn.get('route_distinguishers', '')) with db_api.context_manager.writer.using(context): bgpvpn_db = BGPVPN( id=uuidutils.generate_uuid(), tenant_id=bgpvpn['tenant_id'], name=bgpvpn['name'], type=bgpvpn['type'], route_targets=rt, import_targets=i_rt, export_targets=e_rt, route_distinguishers=rd, vni=bgpvpn.get(bgpvpn_vni_def.VNI), local_pref=bgpvpn.get(bgpvpn_rc_def.LOCAL_PREF_KEY), ) context.session.add(bgpvpn_db) return self._make_bgpvpn_dict(bgpvpn_db) @db_api.context_manager.reader def get_bgpvpns(self, context, filters=None, fields=None): return self._get_collection(context, BGPVPN, self._make_bgpvpn_dict, filters=filters, fields=fields) @db_api.context_manager.reader def _get_bgpvpn(self, context, id): try: return self._get_by_id(context, BGPVPN, id) except exc.NoResultFound: raise bgpvpn_ext.BGPVPNNotFound(id=id) @db_api.context_manager.reader def get_bgpvpn(self, context, id, fields=None): bgpvpn_db = self._get_bgpvpn(context, id) return self._make_bgpvpn_dict(bgpvpn_db, fields) @db_api.context_manager.writer def update_bgpvpn(self, context, id, bgpvpn): bgpvpn_db = self._get_bgpvpn(context, id) if bgpvpn: # Format Route Target lists to string if 'route_targets' in bgpvpn: rt = utils.rtrd_list2str(bgpvpn['route_targets']) bgpvpn['route_targets'] = rt if 'import_targets' in bgpvpn: i_rt = utils.rtrd_list2str(bgpvpn['import_targets']) bgpvpn['import_targets'] = i_rt if 'export_targets' in bgpvpn: e_rt = utils.rtrd_list2str(bgpvpn['export_targets']) bgpvpn['export_targets'] = e_rt if 'route_distinguishers' in bgpvpn: rd = utils.rtrd_list2str(bgpvpn['route_distinguishers']) bgpvpn['route_distinguishers'] = rd bgpvpn_db.update(bgpvpn) return self._make_bgpvpn_dict(bgpvpn_db) @db_api.context_manager.writer def delete_bgpvpn(self, context, id): bgpvpn_db = self._get_bgpvpn(context, id) bgpvpn = self._make_bgpvpn_dict(bgpvpn_db) context.session.delete(bgpvpn_db) return bgpvpn @db_api.context_manager.reader def _make_net_assoc_dict(self, net_assoc_db, fields=None): res = {'id': net_assoc_db['id'], 'tenant_id': net_assoc_db['tenant_id'], 'bgpvpn_id': net_assoc_db['bgpvpn_id'], 'network_id': net_assoc_db['network_id']} return self._fields(res, fields) @db_api.context_manager.reader def _get_net_assoc(self, context, assoc_id, bgpvpn_id): try: query = self._model_query(context, BGPVPNNetAssociation) return query.filter(BGPVPNNetAssociation.id == assoc_id, BGPVPNNetAssociation.bgpvpn_id == bgpvpn_id ).one() except exc.NoResultFound: raise bgpvpn_ext.BGPVPNNetAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) @db_api.context_manager.writer def create_net_assoc(self, context, bgpvpn_id, net_assoc): try: with db_api.context_manager.writer.using(context): net_assoc_db = BGPVPNNetAssociation( tenant_id=net_assoc['tenant_id'], bgpvpn_id=bgpvpn_id, network_id=net_assoc['network_id']) context.session.add(net_assoc_db) return self._make_net_assoc_dict(net_assoc_db) except db_exc.DBDuplicateEntry: LOG.warning("network %(net_id)s is already associated to " "BGPVPN %(bgpvpn_id)s", {'net_id': net_assoc['network_id'], 'bgpvpn_id': bgpvpn_id}) raise bgpvpn_ext.BGPVPNNetAssocAlreadyExists( bgpvpn_id=bgpvpn_id, net_id=net_assoc['network_id']) @db_api.context_manager.reader def get_net_assoc(self, context, assoc_id, bgpvpn_id, fields=None): net_assoc_db = self._get_net_assoc(context, assoc_id, bgpvpn_id) return self._make_net_assoc_dict(net_assoc_db, fields) @db_api.context_manager.reader def get_net_assocs(self, context, bgpvpn_id, filters=None, fields=None): if not filters: filters = {} filters['bgpvpn_id'] = [bgpvpn_id] return self._get_collection(context, BGPVPNNetAssociation, self._make_net_assoc_dict, filters, fields) @db_api.context_manager.writer def delete_net_assoc(self, context, assoc_id, bgpvpn_id): LOG.info("deleting network association %(id)s for " "BGPVPN %(bgpvpn)s", {'id': assoc_id, 'bgpvpn': bgpvpn_id}) net_assoc_db = self._get_net_assoc(context, assoc_id, bgpvpn_id) net_assoc = self._make_net_assoc_dict(net_assoc_db) context.session.delete(net_assoc_db) return net_assoc @db_api.context_manager.reader def _make_router_assoc_dict(self, router_assoc_db, fields=None): res = {'id': router_assoc_db['id'], 'tenant_id': router_assoc_db['tenant_id'], 'bgpvpn_id': router_assoc_db['bgpvpn_id'], 'router_id': router_assoc_db['router_id'], 'advertise_extra_routes': router_assoc_db[ 'advertise_extra_routes'] } return self._fields(res, fields) @db_api.context_manager.reader def _get_router_assoc(self, context, assoc_id, bgpvpn_id): try: query = self._model_query(context, BGPVPNRouterAssociation) return query.filter(BGPVPNRouterAssociation.id == assoc_id, BGPVPNRouterAssociation.bgpvpn_id == bgpvpn_id ).one() except exc.NoResultFound: raise bgpvpn_ext.BGPVPNRouterAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) @db_api.context_manager.writer def create_router_assoc(self, context, bgpvpn_id, router_association): router_id = router_association['router_id'] try: with db_api.context_manager.writer.using(context): router_assoc_db = BGPVPNRouterAssociation( tenant_id=router_association['tenant_id'], bgpvpn_id=bgpvpn_id, router_id=router_id) context.session.add(router_assoc_db) return self._make_router_assoc_dict(router_assoc_db) except db_exc.DBDuplicateEntry: LOG.warning("router %(router_id)s is already associated to " "BGPVPN %(bgpvpn_id)s", {'router_id': router_id, 'bgpvpn_id': bgpvpn_id}) raise bgpvpn_ext.BGPVPNRouterAssocAlreadyExists( bgpvpn_id=bgpvpn_id, router_id=router_association['router_id']) @db_api.context_manager.reader def get_router_assoc(self, context, assoc_id, bgpvpn_id, fields=None): router_assoc_db = self._get_router_assoc(context, assoc_id, bgpvpn_id) return self._make_router_assoc_dict(router_assoc_db, fields) @db_api.context_manager.reader def get_router_assocs(self, context, bgpvpn_id, filters=None, fields=None): if not filters: filters = {} filters['bgpvpn_id'] = [bgpvpn_id] return self._get_collection(context, BGPVPNRouterAssociation, self._make_router_assoc_dict, filters, fields) @db_api.context_manager.writer def update_router_assoc(self, context, assoc_id, bgpvpn_id, router_assoc): router_assoc_db = self._get_router_assoc(context, assoc_id, bgpvpn_id) router_assoc_db.update(router_assoc) return self._make_router_assoc_dict(router_assoc_db) @db_api.context_manager.writer def delete_router_assoc(self, context, assoc_id, bgpvpn_id): LOG.info("deleting router association %(id)s for " "BGPVPN %(bgpvpn)s", {'id': assoc_id, 'bgpvpn': bgpvpn_id}) router_assoc_db = self._get_router_assoc(context, assoc_id, bgpvpn_id) router_assoc = self._make_router_assoc_dict(router_assoc_db) context.session.delete(router_assoc_db) return router_assoc @db_api.context_manager.reader def _make_port_assoc_dict(self, port_assoc_db, fields=None): routes = [port_assoc_route_dict_from_db(r) for r in port_assoc_db['routes']] res = {'id': port_assoc_db['id'], 'tenant_id': port_assoc_db['tenant_id'], 'bgpvpn_id': port_assoc_db['bgpvpn_id'], 'port_id': port_assoc_db['port_id'], 'advertise_fixed_ips': port_assoc_db['advertise_fixed_ips'], 'routes': routes} return self._fields(res, fields) @db_api.context_manager.reader def _get_port_assoc(self, context, assoc_id, bgpvpn_id): try: query = self._model_query(context, BGPVPNPortAssociation) return query.filter(BGPVPNPortAssociation.id == assoc_id, BGPVPNPortAssociation.bgpvpn_id == bgpvpn_id ).one() except exc.NoResultFound: raise bgpvpn_rc_ext.BGPVPNPortAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) @db_api.context_manager.reader def create_port_assoc(self, context, bgpvpn_id, port_association): port_id = port_association['port_id'] advertise_fixed_ips = port_association['advertise_fixed_ips'] try: with db_api.context_manager.writer.using(context): port_assoc_db = BGPVPNPortAssociation( tenant_id=port_association['tenant_id'], bgpvpn_id=bgpvpn_id, port_id=port_id, advertise_fixed_ips=advertise_fixed_ips) context.session.add(port_assoc_db) except db_exc.DBDuplicateEntry: LOG.warning(("port %(port_id)s is already associated to " "BGPVPN %(bgpvpn_id)s"), {'port_id': port_id, 'bgpvpn_id': bgpvpn_id}) raise bgpvpn_rc_ext.BGPVPNPortAssocAlreadyExists( bgpvpn_id=bgpvpn_id, port_id=port_association['port_id']) with db_api.context_manager.writer.using(context): for route in port_association['routes']: _add_port_assoc_route_db_from_dict(context, route, port_assoc_db.id) return self._make_port_assoc_dict(port_assoc_db) @db_api.context_manager.reader def get_port_assoc(self, context, assoc_id, bgpvpn_id, fields=None): port_assoc_db = self._get_port_assoc(context, assoc_id, bgpvpn_id) return self._make_port_assoc_dict(port_assoc_db, fields) @db_api.context_manager.reader def get_port_assocs(self, context, bgpvpn_id, filters=None, fields=None): if not filters: filters = {} filters['bgpvpn_id'] = [bgpvpn_id] return self._get_collection(context, BGPVPNPortAssociation, self._make_port_assoc_dict, filters, fields) @db_api.context_manager.reader def update_port_assoc(self, context, assoc_id, bgpvpn_id, port_assoc): with db_api.context_manager.writer.using(context): port_assoc_db = self._get_port_assoc(context, assoc_id, bgpvpn_id) for route_db in port_assoc_db.routes: context.session.delete(route_db) for route in port_assoc.pop('routes', []): _add_port_assoc_route_db_from_dict(context, route, assoc_id) port_assoc_db.update(port_assoc) context.session.refresh(port_assoc_db) return self._make_port_assoc_dict(port_assoc_db) @db_api.context_manager.writer def delete_port_assoc(self, context, assoc_id, bgpvpn_id): LOG.info(("deleting port association %(id)s for " "BGPVPN %(bgpvpn)s"), {'id': assoc_id, 'bgpvpn': bgpvpn_id}) port_assoc_db = self._get_port_assoc(context, assoc_id, bgpvpn_id) port_assoc = self._make_port_assoc_dict(port_assoc_db) context.session.delete(port_assoc_db) return port_assoc networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/0000775000175100017510000000000013245511747025050 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/0000775000175100017510000000000013245511747030700 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/0000775000175100017510000000000013245511747032550 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEADnetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/CONTRACT_0000666000175100017510000000001513245511235034035 0ustar zuulzuul0000000000000023ce05e0a19f networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/0000775000175100017510000000000013245511747034062 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/expand/networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/ex0000775000175100017510000000000013245511747034417 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022200000000000011211 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/expand/0ab4049986b8_add_indexes_to_tenant_id.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/ex0000666000175100017510000000203613245511235034414 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. # """add indexes to tenant_id Revision ID: 0ab4049986b8 Create Date: 2016-07-22 14:19:04.888614 """ from alembic import op # revision identifiers, used by Alembic. revision = '0ab4049986b8' down_revision = '3600132c6147' def upgrade(): for table in [ 'bgpvpns', 'bgpvpn_network_associations', 'bgpvpn_router_associations', ]: op.create_index(op.f('ix_%s_tenant_id' % table), table, ['tenant_id'], unique=False) ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/contract/networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/co0000775000175100017510000000000013245511747034404 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022400000000000011213 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/contract/23ce05e0a19f_rename_tenant_to_project.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/co0000666000175100017510000000622013245511235034400 0ustar zuulzuul00000000000000# Copyright 2016 Intel Corporation # # 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: 010308b06b49 Create Date: 2016-06-29 19:42:17.862721 """ # revision identifiers, used by Alembic. revision = '23ce05e0a19f' down_revision = '180baa4183e0' depends_on = ('0ab4049986b8',) from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection _INSPECTOR = None def get_inspector(): """Reuse inspector""" global _INSPECTOR if _INSPECTOR: return _INSPECTOR else: 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 hard-coded to match the state of the schema when this upgrade script is run. """ tables = [ 'bgpvpn_router_associations', 'bgpvpns', 'bgpvpn_network_associations', ] 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(): 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() } ././@LongLink0000000000000000000000000000014700000000000011217 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/EXPAND_HEADnetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/EXPAND_HE0000666000175100017510000000001513245511235034014 0ustar zuulzuul00000000000000666c706fea3b networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000775000175100017510000000000013245511747034222 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/e0000775000175100017510000000000013245511747034367 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022700000000000011216 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/3600132c6147_add_router_association_table.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/e0000666000175100017510000000301513245511235034362 0ustar zuulzuul00000000000000# Copyright 2015 Alcatel-Lucent # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """Add router association table Revision ID: 3600132c6147 Revises: 17d9fd4fddee Create Date: 2015-11-16 15:48:31.343859 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '3600132c6147' down_revision = '17d9fd4fddee' def upgrade(): op.create_table( 'bgpvpn_router_associations', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=False), sa.Column('bgpvpn_id', sa.String(36), nullable=False), sa.Column('router_id', sa.String(36), nullable=False), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['bgpvpn_id'], ['bgpvpns.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('bgpvpn_id', 'router_id') ) ././@LongLink0000000000000000000000000000020200000000000011207 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/17d9fd4fddee_initial.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/e0000666000175100017510000000435613245511235034373 0ustar zuulzuul00000000000000# Copyright 2015 Orange # # 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. # """expand initial Revision ID: 17d9fd4fddee Revises: start_networking_bgpvpn Create Date: 2015-10-01 17:35:11.000000 """ from alembic import op import sqlalchemy as sa from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = '17d9fd4fddee' down_revision = 'start_networking_bgpvpn' branch_labels = (cli.EXPAND_BRANCH,) vpn_types = sa.Enum("l2", "l3", name="vpn_types") def upgrade(): op.create_table( 'bgpvpns', sa.Column('name', sa.String(255), nullable=True), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=True), sa.Column('type', vpn_types, nullable=False), sa.Column('route_targets', sa.String(255), nullable=False), sa.Column('import_targets', sa.String(255), nullable=True), sa.Column('export_targets', sa.String(255), nullable=True), sa.Column('route_distinguishers', sa.String(255), nullable=True), sa.PrimaryKeyConstraint('id'), ) op.create_table( 'bgpvpn_network_associations', sa.Column('id', sa.String(length=36), nullable=False), sa.Column('tenant_id', sa.String(length=255), nullable=False), sa.Column('bgpvpn_id', sa.String(36), nullable=False), sa.Column('network_id', sa.String(36), nullable=False), sa.ForeignKeyConstraint(['network_id'], ['networks.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['bgpvpn_id'], ['bgpvpns.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('id'), sa.UniqueConstraint('bgpvpn_id', 'network_id') ) ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/contract/networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/c0000775000175100017510000000000013245511747034365 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000020400000000000011211 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/contract/180baa4183e0_initial.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/c0000666000175100017510000000165113245511235034364 0ustar zuulzuul00000000000000# Copyright 2015 Orange # # 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. # """contract initial Revision ID: 180baa4183e0 Revises: start_networking_bgpvpn Create Date: 2015-10-01 17:35:11.000000 """ from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = '180baa4183e0' down_revision = 'start_networking_bgpvpn' branch_labels = (cli.CONTRACT_BRANCH,) def upgrade(): pass ././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/start_networking_bgpvpn.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/start_net0000666000175100017510000000154513245511235034475 0ustar zuulzuul00000000000000# Copyright 2014 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. # """start networking_bgpvpn chain Revision ID: start_networking_bgpvpn Revises: None Create Date: 2015-10-01 18:04:17.265514 """ # revision identifiers, used by Alembic. revision = 'start_networking_bgpvpn' down_revision = None def upgrade(): pass networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/0000775000175100017510000000000013245511747034050 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/ex0000775000175100017510000000000013245511747034405 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000021300000000000011211 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/666c706fea3b_bgpvpn_local_pref.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/ex0000666000175100017510000000206713245511235034406 0ustar zuulzuul00000000000000# Copyright 2018 # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """ Add local_pref to bgpvpns table Revision ID: 666c706fea3b Revises: 39411aacf9b8 Create Date: 2018-01-18 15:40:05.723129 """ # revision identifiers, used by Alembic. revision = '666c706fea3b' down_revision = '4610803bdf0d' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('bgpvpns', sa.Column('local_pref', sa.BigInteger, nullable=True)) ././@LongLink0000000000000000000000000000022400000000000011213 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/9a6664f3b8d4_add_port_association_table.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/ex0000666000175100017510000000576713245511235034420 0ustar zuulzuul00000000000000# Copyright 2017 # # 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. # """empty message Revision ID: 9a6664f3b8d4 Revises: 0ab4049986b8 Create Date: 2017-06-26 17:34:14.411603 """ # revision identifiers, used by Alembic. revision = '9a6664f3b8d4' down_revision = '0ab4049986b8' from alembic import op import sqlalchemy as sa def upgrade(): op.create_table( 'bgpvpn_port_associations', sa.Column('id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('id'), sa.Column('project_id', sa.String(length=255), index=True, nullable=False), sa.Column('bgpvpn_id', sa.String(36), nullable=False), sa.ForeignKeyConstraint(['bgpvpn_id'], ['bgpvpns.id'], ondelete='CASCADE'), sa.Column('port_id', sa.String(36), nullable=False), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.Column('advertise_fixed_ips', sa.Boolean(), server_default=sa.sql.true(), nullable=False), sa.UniqueConstraint('bgpvpn_id', 'port_id') ) op.create_table( 'bgpvpn_port_association_routes', sa.Column('id', sa.String(length=36), nullable=False), sa.PrimaryKeyConstraint('id'), sa.Column('port_association_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['port_association_id'], ['bgpvpn_port_associations.id'], ondelete='CASCADE'), sa.Column('type', sa.Enum("prefix", "bgpvpn", name="bgpvpn_port_association_route_type"), nullable=False), sa.Column('local_pref', sa.BigInteger(), autoincrement=False, nullable=True), # an IPv6 prefix can be up to 49 chars (IPv4-mapped IPv6 string # representation: up to 45 chars, plus 4 chars for "/128" which is the # highest/longest possible mask) sa.Column('prefix', sa.String(49), nullable=True), sa.Column('bgpvpn_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['bgpvpn_id'], ['bgpvpns.id'], ondelete='CASCADE') # NOTE(tmorin): it would be nice to add some CheckConstraint so that # prefix and bgpvpn_id are enforced as NULL unless relevant for the # type, and non-NULL when relevant for the type ) ././@LongLink0000000000000000000000000000022100000000000011210 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/39411aacf9b8_add_vni_to_bgpvpn_table.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/ex0000666000175100017510000000171613245511235034406 0ustar zuulzuul00000000000000# # Copyright 2017 Ericsson India Global Services 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. # """add vni to bgpvpn table Revision ID: 39411aacf9b8 Revises: 9a6664f3b8d4 Create Date: 2017-09-19 17:37:11.359338 """ # revision identifiers, used by Alembic. revision = '39411aacf9b8' down_revision = '9a6664f3b8d4' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('bgpvpns', sa.Column('vni', sa.Integer)) ././@LongLink0000000000000000000000000000024100000000000011212 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/4610803bdf0d_router_assoc_add_advertise_extra_routes.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/ex0000666000175100017510000000213113245511235034376 0ustar zuulzuul00000000000000# Copyright 2017 # # 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. # """empty message Revision ID: 4610803bdf0d Revises: 9a6664f3b8d4 Create Date: 2017-06-26 17:39:11.086696 """ # revision identifiers, used by Alembic. revision = '4610803bdf0d' down_revision = '39411aacf9b8' from alembic import op import sqlalchemy as sa def upgrade(): op.add_column('bgpvpn_router_associations', sa.Column('advertise_extra_routes', sa.Boolean(), nullable=False, server_default=sa.true())) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/env.py0000666000175100017510000000466513245511235032047 0ustar zuulzuul00000000000000# Copyright 2014 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. 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 MYSQL_ENGINE = None BGPVPN_VERSION_TABLE = 'alembic_version_bgpvpn' 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'] = BGPVPN_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=BGPVPN_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() networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/README0000666000175100017510000000004613245511235031552 0ustar zuulzuul00000000000000Generic single-database configuration.networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/__init__.py0000666000175100017510000000000013245511235032771 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/script.py.mako0000666000175100017510000000203713245511235033500 0ustar zuulzuul00000000000000# Copyright ${create_date.year} # # 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"} networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/db/migration/__init__.py0000666000175100017510000000000013245511235027141 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/opts.py0000666000175100017510000000254313245511235024027 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.conf.services import provider_configuration from oslo_config import cfg from networking_bgpvpn.neutron.services.service_drivers.opencontrail \ import opencontrail_client def list_service_provider(): return [ ('service_providers', provider_configuration.serviceprovider_opts), ] _dummy_bgpvpn_provider = ':'.join([ 'BGPVPN', 'Dummy', 'networking_bgpvpn.neutron.services.service_drivers.driver_api.' 'BGPVPNDriver', 'default' ]) # Set reasonable example for BGPVPN as a default value def set_service_provider_default(): cfg.set_defaults(provider_configuration.serviceprovider_opts, service_provider=[_dummy_bgpvpn_provider]) def list_opencontrail_driver_opts(): return [ ('apiserver', opencontrail_client.opencontrail_opts), ] networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/__init__.py0000666000175100017510000000000013245511235024563 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/0000775000175100017510000000000013245511747024315 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/common/0000775000175100017510000000000013245511747025605 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/common/__init__.py0000666000175100017510000000000013245511235027676 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/common/utils.py0000666000175100017510000001035313245511235027313 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cloudwatt. # 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.api.definitions import bgpvpn as bgpvpn_def from neutron_lib.api.definitions import bgpvpn_routes_control as bgpvpn_rc_def from neutron_lib.api.definitions import bgpvpn_vni as bgpvpn_vni_def from neutron_lib.plugins import directory def rtrd_list2str(list): """Format Route Target list to string""" if not list: return '' if isinstance(list, str): return list return ','.join(list) def rtrd_str2list(str): """Format Route Target string to list""" if not str: return [] if isinstance(str, list): return str return str.split(',') def filter_resource(resource, filters=None): if not filters: filters = {} for key, value in filters.items(): if key in resource.keys(): if not isinstance(value, list): value = [value] if isinstance(resource[key], list): resource_value = resource[key] else: resource_value = [resource[key]] if not set(value).issubset(set(resource_value)): return False return True def filter_fields(resource, fields): if fields: return dict(((key, item) for key, item in resource.items() if key in fields)) return resource def is_extension_supported(plugin, ext_alias): return ext_alias in plugin.supported_extension_aliases def make_bgpvpn_dict(bgpvpn, fields=None): res = { 'id': bgpvpn['id'], 'tenant_id': bgpvpn['tenant_id'], 'name': bgpvpn['name'], 'type': bgpvpn['type'], 'route_targets': rtrd_str2list(bgpvpn['route_targets']), 'import_targets': rtrd_str2list(bgpvpn['import_targets']), 'export_targets': rtrd_str2list(bgpvpn['export_targets']), 'route_distinguishers': rtrd_str2list(bgpvpn['route_distinguishers']), 'networks': bgpvpn.get('networks', []), 'routers': bgpvpn.get('routers', []), 'ports': bgpvpn.get('ports', []), } plugin = directory.get_plugin(bgpvpn_def.LABEL) if is_extension_supported(plugin, bgpvpn_vni_def.ALIAS): res[bgpvpn_vni_def.VNI] = bgpvpn.get(bgpvpn_vni_def.VNI) if is_extension_supported(plugin, bgpvpn_rc_def.ALIAS): res[bgpvpn_rc_def.LOCAL_PREF_KEY] = bgpvpn.get( bgpvpn_rc_def.LOCAL_PREF_KEY) return filter_fields(res, fields) def make_net_assoc_dict(id, tenant_id, bgpvpn_id, network_id, fields=None): res = {'id': id, 'tenant_id': tenant_id, 'bgpvpn_id': bgpvpn_id, 'network_id': network_id} return filter_fields(res, fields) def make_router_assoc_dict(id, tenant_id, bgpvpn_id, router_id, fields=None): res = {'id': id, 'tenant_id': tenant_id, 'bgpvpn_id': bgpvpn_id, 'router_id': router_id} return filter_fields(res, fields) def make_port_assoc_dict(id, tenant_id, bgpvpn_id, port_id, fields=None): # NOTE(tmorin): fields need to be added here, this isn't used yet res = {'id': id, 'tenant_id': tenant_id, 'bgpvpn_id': bgpvpn_id, 'port_id': port_id} return filter_fields(res, fields) def get_bgpvpn_differences(current_dict, old_dict): """Compare 2 BGP VPN - added keys - removed keys - changed values for keys in both dictionaries """ set_current = set(current_dict.keys()) set_old = set(old_dict.keys()) intersect = set_current.intersection(set_old) added = set_current - intersect removed = set_old - intersect changed = set( key for key in intersect if old_dict[key] != current_dict[key] ) return (added, removed, changed) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/common/constants.py0000777000175100017510000000173213245511235030173 0ustar zuulzuul00000000000000# Copyright 2015 OpenStack Foundation # Copyright (c) 2015 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. from neutron_lib.api.definitions import bgpvpn BGPVPN_RES = bgpvpn.BGPVPN_RES BGPVPN_L3 = bgpvpn.BGPVPN_L3 BGPVPN_L2 = bgpvpn.BGPVPN_L2 BGPVPN_TYPES = bgpvpn.BGPVPN_TYPES UINT32_REGEX, = bgpvpn.UINT32_REGEX, UINT16_REGEX = bgpvpn.UINT16_REGEX UINT8_REGEX = bgpvpn.UINT8_REGEX IP4_REGEX = bgpvpn.IP4_REGEX RTRD_REGEX = bgpvpn.RTRD_REGEX networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/plugin.py0000666000175100017510000003432013245511271026161 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 copy from neutron.db import servicetype_db as st_db from neutron.services import provider_configuration as pconf from neutron.services import service_base from neutron_lib.api.definitions import bgpvpn as bgpvpn_def from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as 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_log import log from networking_bgpvpn._i18n import _ from networking_bgpvpn.neutron.extensions import bgpvpn from networking_bgpvpn.neutron.extensions \ import bgpvpn_routes_control as bgpvpn_rc from networking_bgpvpn.neutron.services.common import constants LOG = log.getLogger(__name__) class BGPVPNPlugin(bgpvpn.BGPVPNPluginBase, bgpvpn_rc.BGPVPNRoutesControlPluginBase): def __init__(self): super(BGPVPNPlugin, self).__init__() # Need to look into /etc/neutron/networking_bgpvpn.conf for # service_provider definitions: service_type_manager = st_db.ServiceTypeManager.get_instance() service_type_manager.add_provider_configuration( bgpvpn_def.LABEL, pconf.ProviderConfiguration('networking_bgpvpn')) # Load the default driver drivers, default_provider = service_base.load_drivers(bgpvpn_def.LABEL, self) LOG.info("BGP VPN Service Plugin using Service Driver: %s", default_provider) self.driver = drivers[default_provider] if len(drivers) > 1: LOG.warning("Multiple drivers configured for BGPVPN, although" "running multiple drivers in parallel is not yet" "supported") registry.subscribe(self._notify_adding_interface_to_router, resources.ROUTER_INTERFACE, events.BEFORE_CREATE) @property def supported_extension_aliases(self): exts = copy.copy(super(BGPVPNPlugin, self).supported_extension_aliases) exts += self.driver.more_supported_extension_aliases return exts def _notify_adding_interface_to_router(self, resource, event, trigger, **kwargs): context = kwargs.get('context') network_id = kwargs.get('network_id') router_id = kwargs.get('router_id') try: routers_bgpvpns = self.driver.get_bgpvpns( context, filters={ 'routers': [router_id], }, ) except bgpvpn.BGPVPNRouterAssociationNotSupported: return nets_bgpvpns = self.driver.get_bgpvpns( context, filters={ 'networks': [network_id], 'type': [constants.BGPVPN_L3], }, ) if routers_bgpvpns and nets_bgpvpns: msg = _('It is not allowed to add an interface to a router if ' 'both the router and the network are bound to an ' 'L3 BGPVPN.') raise n_exc.BadRequest(resource='bgpvpn', msg=msg) def _validate_network(self, context, net_id): plugin = directory.get_plugin() network = plugin.get_network(context, net_id) self._validate_network_has_router_assoc(context, network, plugin) return network def _validate_network_has_router_assoc(self, context, network, plugin): filter = {'network_id': [network['id']], 'device_owner': [const.DEVICE_OWNER_ROUTER_INTF]} router_port = plugin.get_ports(context, filters=filter) if router_port: router_id = router_port[0]['device_id'] filter = {'tenant_id': [network['tenant_id']]} bgpvpns = self.driver.get_bgpvpns(context, filters=filter) bgpvpns = [str(bgpvpn['id']) for bgpvpn in bgpvpns if router_id in bgpvpn['routers']] if bgpvpns: msg = ('Network %(net_id)s is linked to a router which is ' 'already associated to bgpvpn(s) %(bgpvpns)s' % {'net_id': network['id'], 'bgpvpns': bgpvpns} ) raise n_exc.BadRequest(resource='bgpvpn', msg=msg) def _validate_router(self, context, router_id): l3_plugin = directory.get_plugin(plugin_constants.L3) router = l3_plugin.get_router(context, router_id) plugin = directory.get_plugin() self._validate_router_has_net_assocs(context, router, plugin) return router def _validate_port(self, context, port_id): plugin = directory.get_plugin() port = plugin.get_port(context, port_id) return port def _validate_router_has_net_assocs(self, context, router, plugin): filter = {'device_id': [router['id']], 'device_owner': [const.DEVICE_OWNER_ROUTER_INTF]} router_ports = plugin.get_ports(context, filters=filter) if router_ports: filter = {'tenant_id': [router['tenant_id']]} bgpvpns = self.driver.get_bgpvpns(context, filters=filter) for port in router_ports: bgpvpns = [str(bgpvpn['id']) for bgpvpn in bgpvpns if port['network_id'] in bgpvpn['networks']] if bgpvpns: msg = ('router %(rtr_id)s has an attached network ' '%(net_id)s which is already associated to ' 'bgpvpn(s) %(bgpvpns)s' % {'rtr_id': router['id'], 'net_id': port['network_id'], 'bgpvpns': bgpvpns}) raise n_exc.BadRequest(resource='bgpvpn', msg=msg) def get_plugin_type(self): return bgpvpn_def.LABEL def get_plugin_description(self): return "Neutron BGPVPN Service Plugin" def create_bgpvpn(self, context, bgpvpn): bgpvpn = bgpvpn['bgpvpn'] return self.driver.create_bgpvpn(context, bgpvpn) def get_bgpvpns(self, context, filters=None, fields=None): return self.driver.get_bgpvpns(context, filters, fields) def get_bgpvpn(self, context, id, fields=None): return self.driver.get_bgpvpn(context, id, fields) def update_bgpvpn(self, context, id, bgpvpn): bgpvpn = bgpvpn['bgpvpn'] return self.driver.update_bgpvpn(context, id, bgpvpn) def delete_bgpvpn(self, context, id): self.driver.delete_bgpvpn(context, id) def create_bgpvpn_network_association(self, context, bgpvpn_id, network_association): net_assoc = network_association['network_association'] # check net exists net = self._validate_network(context, net_assoc['network_id']) # check every resource belong to the same tenant bgpvpn = self.get_bgpvpn(context, bgpvpn_id) if net['tenant_id'] != bgpvpn['tenant_id']: msg = 'network doesn\'t belong to the bgpvpn owner' raise n_exc.NotAuthorized(resource='bgpvpn', msg=msg) if net_assoc['tenant_id'] != bgpvpn['tenant_id']: msg = 'network association and bgpvpn should belong to\ the same tenant' raise n_exc.NotAuthorized(resource='bgpvpn', msg=msg) return self.driver.create_net_assoc(context, bgpvpn_id, net_assoc) def get_bgpvpn_network_association(self, context, assoc_id, bgpvpn_id, fields=None): return self.driver.get_net_assoc(context, assoc_id, bgpvpn_id, fields) def get_bgpvpn_network_associations(self, context, bgpvpn_id, filters=None, fields=None): return self.driver.get_net_assocs(context, bgpvpn_id, filters, fields) def update_bgpvpn_network_association(self, context, assoc_id, bgpvpn_id, network_association): # TODO(matrohon) : raise an unsuppported error pass def delete_bgpvpn_network_association(self, context, assoc_id, bgpvpn_id): self.driver.delete_net_assoc(context, assoc_id, bgpvpn_id) def create_bgpvpn_router_association(self, context, bgpvpn_id, router_association): router_assoc = router_association['router_association'] router = self._validate_router(context, router_assoc['router_id']) bgpvpn = self.get_bgpvpn(context, bgpvpn_id) if not bgpvpn['type'] == constants.BGPVPN_L3: msg = ("Router associations require the bgpvpn to be of type %s" % constants.BGPVPN_L3) raise n_exc.BadRequest(resource='bgpvpn', msg=msg) if not router['tenant_id'] == bgpvpn['tenant_id']: msg = "router doesn't belong to the bgpvpn owner" raise n_exc.NotAuthorized(resource='bgpvpn', msg=msg) if not (router_assoc['tenant_id'] == bgpvpn['tenant_id']): msg = "router association and bgpvpn should " \ "belong to the same tenant" raise n_exc.NotAuthorized(resource='bgpvpn', msg=msg) return self.driver.create_router_assoc(context, bgpvpn_id, router_assoc) def get_bgpvpn_router_association(self, context, assoc_id, bgpvpn_id, fields=None): return self.driver.get_router_assoc(context, assoc_id, bgpvpn_id, fields) def get_bgpvpn_router_associations(self, context, bgpvpn_id, filters=None, fields=None): return self.driver.get_router_assocs(context, bgpvpn_id, filters, fields) def update_bgpvpn_router_association(self, context, assoc_id, bgpvpn_id, router_association): router_association = router_association['router_association'] return self.driver.update_router_assoc(context, assoc_id, bgpvpn_id, router_association) def delete_bgpvpn_router_association(self, context, assoc_id, bgpvpn_id): self.driver.delete_router_assoc(context, assoc_id, bgpvpn_id) def _validate_port_association_routes_bgpvpn(self, context, port_association, bgpvpn_id, assoc_id=None): for route in [r for r in port_association.get('routes', []) if r['type'] == bgpvpn_rc.api_def.BGPVPN_TYPE]: try: route_bgpvpn = self.get_bgpvpn(context, route['bgpvpn_id']) except bgpvpn.BGPVPNNotFound: raise bgpvpn_rc.BGPVPNPortAssocRouteNoSuchBGPVPN( bgpvpn_id=route['bgpvpn_id']) assoc_tenant_id = port_association.get('project_id') if assoc_tenant_id is None: # update, rather than create, we need to retrieve the tenant assoc = self.get_bgpvpn_port_association(context, assoc_id, bgpvpn_id) assoc_tenant_id = assoc.get('project_id') if route_bgpvpn['project_id'] != assoc_tenant_id: raise bgpvpn_rc.BGPVPNPortAssocRouteWrongBGPVPNTenant( bgpvpn_id=route['bgpvpn_id']) def create_bgpvpn_port_association(self, context, bgpvpn_id, port_association): port_association = port_association['port_association'] port = self._validate_port(context, port_association['port_id']) bgpvpn = self.get_bgpvpn(context, bgpvpn_id) if not port['tenant_id'] == bgpvpn['project_id']: msg = "port doesn't belong to the bgpvpn owner" raise n_exc.NotAuthorized(resource='bgpvpn', msg=msg) if not (port_association['project_id'] == bgpvpn['project_id']): msg = "port association and bgpvpn should " \ "belong to the same tenant" raise n_exc.NotAuthorized(resource='bgpvpn', msg=msg) self._validate_port_association_routes_bgpvpn(context, port_association, bgpvpn_id) return self.driver.create_port_assoc(context, bgpvpn_id, port_association) def get_bgpvpn_port_association(self, context, assoc_id, bgpvpn_id, fields=None): return self.driver.get_port_assoc(context, assoc_id, bgpvpn_id, fields) def get_bgpvpn_port_associations(self, context, bgpvpn_id, filters=None, fields=None): return self.driver.get_port_assocs(context, bgpvpn_id, filters, fields) def update_bgpvpn_port_association(self, context, assoc_id, bgpvpn_id, port_association): port_association = port_association['port_association'] self._validate_port_association_routes_bgpvpn(context, port_association, bgpvpn_id, assoc_id) return self.driver.update_port_assoc(context, assoc_id, bgpvpn_id, port_association) def delete_bgpvpn_port_association(self, context, assoc_id, bgpvpn_id): self.driver.delete_port_assoc(context, assoc_id, bgpvpn_id) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/0000775000175100017510000000000013245511747027513 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opendaylight/0000775000175100017510000000000013245511747032202 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opendaylight/odl.py0000666000175100017510000001227613245511235033334 0ustar zuulzuul00000000000000# # Copyright (C) 2015 Ericsson India Global Services 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 requests from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils from networking_odl.common import client as odl_client from networking_bgpvpn.neutron.extensions import bgpvpn as bgpvpn_ext from networking_bgpvpn.neutron.services.service_drivers import driver_api cfg.CONF.import_group('ml2_odl', 'networking_odl.common.config') LOG = logging.getLogger(__name__) BGPVPNS = 'bgpvpns' OPENDAYLIGHT_BGPVPN_DRIVER_NAME = 'OpenDaylight' class OpenDaylightBgpvpnDriver(driver_api.BGPVPNDriver): """OpenDaylight BGPVPN Driver This code is the backend implementation for the OpenDaylight BGPVPN driver for Openstack Neutron. """ def __init__(self, service_plugin): LOG.warning("This OpenDaylight BGPVPN driver has been deprecated" "and will be removed. Switch to new v2 Driver: " "BGPVPN:OpenDaylight:networking_odl.bgpvpn.odl_v2." "OpenDaylightBgpvpnDriver:default") super(OpenDaylightBgpvpnDriver, self).__init__(service_plugin) self.service_plugin = service_plugin self.client = odl_client.OpenDaylightRestClient.create_client() def create_bgpvpn_precommit(self, context, bgpvpn): pass def create_bgpvpn_postcommit(self, context, bgpvpn): url = BGPVPNS try: self.client.sendjson('post', url, {BGPVPNS[:-1]: bgpvpn}) except requests.exceptions.RequestException: with excutils.save_and_reraise_exception(): # delete from db d_bgpvpn = self.bgpvpn_db.delete_bgpvpn(context, bgpvpn['id']) LOG.debug("Deleted bgpvpn %s from db", d_bgpvpn) def delete_bgpvpn_postcommit(self, context, bgpvpn): url = BGPVPNS + '/' + bgpvpn['id'] self.client.sendjson('delete', url, None) def update_bgpvpn_postcommit(self, context, old_bgpvpn, bgpvpn): url = BGPVPNS + '/' + bgpvpn['id'] self.client.sendjson('put', url, {BGPVPNS[:-1]: bgpvpn}) def create_net_assoc_precommit(self, context, net_assoc): bgpvpns = self.bgpvpn_db.get_bgpvpns( context, filters={ 'networks': [net_assoc['network_id']], }, ) if len(bgpvpns) > 1: raise bgpvpn_ext.BGPVPNNetworkAssocExistsAnotherBgpvpn( driver=OPENDAYLIGHT_BGPVPN_DRIVER_NAME, network=net_assoc['network_id'], bgpvpn=bgpvpns[0]['id']) def create_net_assoc_postcommit(self, context, net_assoc): bgpvpn = self.get_bgpvpn(context, net_assoc['bgpvpn_id']) url = BGPVPNS + '/' + bgpvpn['id'] try: self.client.sendjson('put', url, {BGPVPNS[:-1]: bgpvpn}) except requests.exceptions.RequestException: with excutils.save_and_reraise_exception(): # delete from db d_netassoc = self.bgpvpn_db.delete_net_assoc( context, net_assoc['id'], net_assoc['bgpvpn_id']) LOG.debug("Deleted net_assoc %s from db", d_netassoc) def delete_net_assoc_postcommit(self, context, net_assoc): bgpvpn = self.get_bgpvpn(context, net_assoc['bgpvpn_id']) url = BGPVPNS + '/' + bgpvpn['id'] self.client.sendjson('put', url, {BGPVPNS[:-1]: bgpvpn}) def create_router_assoc_precommit(self, context, router_assoc): associated_routers = self.get_router_assocs(context, router_assoc['bgpvpn_id']) for assoc_router in associated_routers: if(router_assoc["router_id"] != assoc_router["router_id"]): raise bgpvpn_ext.BGPVPNMultipleRouterAssocNotSupported( driver=OPENDAYLIGHT_BGPVPN_DRIVER_NAME) def create_router_assoc_postcommit(self, context, router_assoc): bgpvpn = self.get_bgpvpn(context, router_assoc['bgpvpn_id']) url = BGPVPNS + '/' + bgpvpn['id'] try: self.client.sendjson('put', url, {BGPVPNS[:-1]: bgpvpn}) except requests.exceptions.RequestException: with excutils.save_and_reraise_exception(): # delete from db d_routerassoc = self.bgpvpn_db.delete_router_assoc( context, router_assoc['id'], router_assoc['bgpvpn_id']) LOG.debug("Deleted router_assoc %s from db", d_routerassoc) def delete_router_assoc_postcommit(self, context, router_assoc): bgpvpn = self.get_bgpvpn(context, router_assoc['bgpvpn_id']) url = BGPVPNS + '/' + bgpvpn['id'] self.client.sendjson('put', url, {BGPVPNS[:-1]: bgpvpn}) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opendaylight/__init__.py0000666000175100017510000000000013245511235034273 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/driver_api.py0000666000175100017510000004015313245511235032206 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 import copy import six from neutron.db import api as db_api from networking_bgpvpn.neutron.db import bgpvpn_db from networking_bgpvpn.neutron.extensions \ import bgpvpn_routes_control as bgpvpn_rc @six.add_metaclass(abc.ABCMeta) class BGPVPNDriverBase(object): """BGPVPNDriver interface for driver That driver interface does not persist BGPVPN data in any database. The driver needs to do it by itself. """ more_supported_extension_aliases = [] def __init__(self, service_plugin): self.service_plugin = service_plugin @property def service_type(self): pass @abc.abstractmethod def create_bgpvpn(self, context, bgpvpn): pass @abc.abstractmethod def get_bgpvpns(self, context, filters=None, fields=None): pass @abc.abstractmethod def get_bgpvpn(self, context, id, fields=None): pass @abc.abstractmethod def update_bgpvpn(self, context, id, bgpvpn): pass @abc.abstractmethod def delete_bgpvpn(self, context, id): pass @abc.abstractmethod def create_net_assoc(self, bgpvpn_id, network_association): pass @abc.abstractmethod def get_net_assoc(self, context, assoc_id, bgpvpn_id, fields=None): pass @abc.abstractmethod def get_net_assocs(self, context, bgpvpn_id, filters=None, fields=None): pass @abc.abstractmethod def delete_net_assoc(self, context, assoc_id, bgpvpn_id): pass @abc.abstractmethod def create_router_assoc(self, context, bgpvpn_id, router_association): pass @abc.abstractmethod def get_router_assoc(self, context, assoc_id, bgpvpn_id, fields=None): pass @abc.abstractmethod def get_router_assocs(self, context, bgpvpn_id, filters=None, fields=None): pass @abc.abstractmethod def delete_router_assoc(self, context, assoc_id, bgpvpn_id): pass @six.add_metaclass(abc.ABCMeta) class BGPVPNDriverDBMixin(BGPVPNDriverBase): """BGPVPNDriverDB Mixin to provision the database on behalf of the driver That driver interface persists BGPVPN data in its database and forwards the result to postcommit methods """ def __init__(self, *args, **kwargs): super(BGPVPNDriverDBMixin, self).__init__(*args, **kwargs) self.bgpvpn_db = bgpvpn_db.BGPVPNPluginDb() def create_bgpvpn(self, context, bgpvpn): with db_api.context_manager.writer.using(context): bgpvpn = self.bgpvpn_db.create_bgpvpn( context, bgpvpn) self.create_bgpvpn_precommit(context, bgpvpn) self.create_bgpvpn_postcommit(context, bgpvpn) return bgpvpn def get_bgpvpns(self, context, filters=None, fields=None): return self.bgpvpn_db.get_bgpvpns(context, filters, fields) def get_bgpvpn(self, context, id, fields=None): return self.bgpvpn_db.get_bgpvpn(context, id, fields) def update_bgpvpn(self, context, id, bgpvpn_delta): old_bgpvpn = self.get_bgpvpn(context, id) with db_api.context_manager.writer.using(context): new_bgpvpn = copy.deepcopy(old_bgpvpn) new_bgpvpn.update(bgpvpn_delta) self.update_bgpvpn_precommit(context, old_bgpvpn, new_bgpvpn) bgpvpn = self.bgpvpn_db.update_bgpvpn(context, id, bgpvpn_delta) self.update_bgpvpn_postcommit(context, old_bgpvpn, bgpvpn) return bgpvpn def delete_bgpvpn(self, context, id): with db_api.context_manager.writer.using(context): bgpvpn = self.bgpvpn_db.get_bgpvpn(context, id) self.delete_bgpvpn_precommit(context, bgpvpn) self.bgpvpn_db.delete_bgpvpn(context, id) self.delete_bgpvpn_postcommit(context, bgpvpn) def create_net_assoc(self, context, bgpvpn_id, network_association): with db_api.context_manager.writer.using(context): assoc = self.bgpvpn_db.create_net_assoc(context, bgpvpn_id, network_association) self.create_net_assoc_precommit(context, assoc) self.create_net_assoc_postcommit(context, assoc) return assoc def get_net_assoc(self, context, assoc_id, bgpvpn_id, fields=None): return self.bgpvpn_db.get_net_assoc(context, assoc_id, bgpvpn_id, fields) def get_net_assocs(self, context, bgpvpn_id, filters=None, fields=None): return self.bgpvpn_db.get_net_assocs(context, bgpvpn_id, filters, fields) def delete_net_assoc(self, context, assoc_id, bgpvpn_id): with db_api.context_manager.writer.using(context): net_assoc = self.bgpvpn_db.get_net_assoc(context, assoc_id, bgpvpn_id) self.delete_net_assoc_precommit(context, net_assoc) self.bgpvpn_db.delete_net_assoc(context, assoc_id, bgpvpn_id) self.delete_net_assoc_postcommit(context, net_assoc) def create_router_assoc(self, context, bgpvpn_id, router_association): with db_api.context_manager.writer.using(context): assoc = self.bgpvpn_db.create_router_assoc(context, bgpvpn_id, router_association) self.create_router_assoc_precommit(context, assoc) self.create_router_assoc_postcommit(context, assoc) return assoc def get_router_assoc(self, context, assoc_id, bgpvpn_id, fields=None): return self.bgpvpn_db.get_router_assoc(context, assoc_id, bgpvpn_id, fields) def get_router_assocs(self, context, bgpvpn_id, filters=None, fields=None): return self.bgpvpn_db.get_router_assocs(context, bgpvpn_id, filters, fields) def delete_router_assoc(self, context, assoc_id, bgpvpn_id): with db_api.context_manager.writer.using(context): router_assoc = self.bgpvpn_db.get_router_assoc(context, assoc_id, bgpvpn_id) self.delete_router_assoc_precommit(context, router_assoc) self.bgpvpn_db.delete_router_assoc(context, assoc_id, bgpvpn_id) self.delete_router_assoc_postcommit(context, router_assoc) @abc.abstractmethod def create_bgpvpn_postcommit(self, context, bgpvpn): pass @abc.abstractmethod def create_bgpvpn_precommit(self, context, bgpvpn): pass @abc.abstractmethod def update_bgpvpn_postcommit(self, context, old_bgpvpn, new_bgpvpn): pass @abc.abstractmethod def update_bgpvpn_precommit(self, context, old_bgpvpn, new_bgpvpn): pass @abc.abstractmethod def delete_bgpvpn_postcommit(self, context, bgpvpn): pass @abc.abstractmethod def create_net_assoc_precommit(self, context, net_assoc): pass @abc.abstractmethod def create_net_assoc_postcommit(self, context, net_assoc): pass @abc.abstractmethod def delete_net_assoc_precommit(self, context, net_assoc): pass @abc.abstractmethod def delete_net_assoc_postcommit(self, context, net_assoc): pass @abc.abstractmethod def create_router_assoc_precommit(self, context, router_assoc): pass @abc.abstractmethod def create_router_assoc_postcommit(self, context, router_assoc): pass @abc.abstractmethod def delete_router_assoc_precommit(self, context, router_assoc): pass @abc.abstractmethod def delete_router_assoc_postcommit(self, context, router_assoc): pass class BGPVPNDriver(BGPVPNDriverDBMixin): """BGPVPNDriver interface for driver with database. Each bgpvpn driver that needs a database persistency should inherit from this driver. It can overload needed methods from the following pre/postcommit methods. Any exception raised during a precommit method will result in not having related records in the databases. """ def create_bgpvpn_precommit(self, context, bgpvpn): pass def create_bgpvpn_postcommit(self, context, bgpvpn): pass def update_bgpvpn_precommit(self, context, old_bgpvpn, new_bgpvpn): pass def update_bgpvpn_postcommit(self, context, old_bgpvpn, new_bgpvpn): pass def delete_bgpvpn_precommit(self, context, bgpvpn): pass def delete_bgpvpn_postcommit(self, context, bgpvpn): pass def create_net_assoc_precommit(self, context, net_assoc): pass def create_net_assoc_postcommit(self, context, net_assoc): pass def delete_net_assoc_precommit(self, context, net_assoc): pass def delete_net_assoc_postcommit(self, context, net_assoc): pass def create_router_assoc_precommit(self, context, router_assoc): pass def create_router_assoc_postcommit(self, context, router_assoc): pass def delete_router_assoc_precommit(self, context, router_assoc): pass def delete_router_assoc_postcommit(self, context, router_assoc): pass @six.add_metaclass(abc.ABCMeta) class BGPVPNDriverRCBase(BGPVPNDriverBase): """Base class for drivers implementing the bgpvpn-routes-control API ext""" more_supported_extension_aliases = [ bgpvpn_rc.Bgpvpn_routes_control.get_alias()] def __init__(self, *args, **kwargs): super(BGPVPNDriverRCBase, self).__init__(*args, **kwargs) @abc.abstractmethod def update_router_assoc(self, context, assoc_id, router_association): pass @abc.abstractmethod def create_port_assoc(self, bgpvpn_id, port_association): pass @abc.abstractmethod def get_port_assoc(self, context, assoc_id, bgpvpn_id, fields=None): pass @abc.abstractmethod def get_port_assocs(self, context, bgpvpn_id, filters=None, fields=None): pass @abc.abstractmethod def update_port_assoc(self, context, assoc_id, port_association): pass @abc.abstractmethod def delete_port_assoc(self, context, assoc_id, bgpvpn_id): pass @six.add_metaclass(abc.ABCMeta) class BGPVPNDriverRCDBMixin(BGPVPNDriverRCBase, BGPVPNDriverDBMixin): """BGPVPNDriverDBMixin with DB operations for bgpvpn-route-control ext.""" def __init__(self, *args, **xargs): BGPVPNDriverDBMixin.__init__(self, *args, **xargs) def update_router_assoc(self, context, assoc_id, bgpvpn_id, router_assoc): old_router_assoc = self.get_router_assoc(context, assoc_id, bgpvpn_id) with db_api.context_manager.writer.using(context): router_assoc = self.bgpvpn_db.update_router_assoc(context, assoc_id, bgpvpn_id, router_assoc) self.update_router_assoc_precommit(context, old_router_assoc, router_assoc) self.update_router_assoc_postcommit(context, old_router_assoc, router_assoc) return router_assoc @abc.abstractmethod def update_router_assoc_precommit(self, context, old_router_assoc, router_assoc): pass @abc.abstractmethod def update_router_assoc_postcommit(self, context, old_router_assoc, router_assoc): pass def create_port_assoc(self, context, bgpvpn_id, port_association): with db_api.context_manager.writer.using(context): port_assoc = self.bgpvpn_db.create_port_assoc(context, bgpvpn_id, port_association) self.create_port_assoc_precommit(context, port_assoc) self.create_port_assoc_postcommit(context, port_assoc) return port_assoc @abc.abstractmethod def create_port_assoc_precommit(self, context, port_assoc): pass @abc.abstractmethod def create_port_assoc_postcommit(self, context, port_assoc): pass def get_port_assoc(self, context, assoc_id, bgpvpn_id, fields=None): return self.bgpvpn_db.get_port_assoc(context, assoc_id, bgpvpn_id, fields) def get_port_assocs(self, context, bgpvpn_id, filters=None, fields=None): return self.bgpvpn_db.get_port_assocs(context, bgpvpn_id, filters, fields) def update_port_assoc(self, context, assoc_id, bgpvpn_id, port_assoc): old_port_assoc = self.get_port_assoc(context, assoc_id, bgpvpn_id) with db_api.context_manager.writer.using(context): port_assoc = self.bgpvpn_db.update_port_assoc(context, assoc_id, bgpvpn_id, port_assoc) self.update_port_assoc_precommit(context, old_port_assoc, port_assoc) self.update_port_assoc_postcommit(context, old_port_assoc, port_assoc) return port_assoc @abc.abstractmethod def update_port_assoc_precommit(self, context, old_port_assoc, port_assoc): pass @abc.abstractmethod def update_port_assoc_postcommit(self, context, old_port_assoc, port_assoc): pass def delete_port_assoc(self, context, assoc_id, bgpvpn_id): with db_api.context_manager.writer.using(context): port_assoc = self.bgpvpn_db.get_port_assoc(context, assoc_id, bgpvpn_id) self.delete_port_assoc_precommit(context, port_assoc) self.bgpvpn_db.delete_port_assoc(context, assoc_id, bgpvpn_id) self.delete_port_assoc_postcommit(context, port_assoc) @abc.abstractmethod def delete_port_assoc_precommit(self, context, port_assoc): pass @abc.abstractmethod def delete_port_assoc_postcommit(self, context, port_assoc): pass class BGPVPNDriverRC(BGPVPNDriverRCDBMixin, BGPVPNDriver): """Base class for a DB driver supporting bgpvpn-routes-control API ext.""" def update_router_assoc_precommit(self, context, old_router_assoc, router_assoc): pass def update_router_assoc_postcommit(self, context, old_router_assoc, router_assoc): pass def create_port_assoc_precommit(self, context, port_assoc): pass def create_port_assoc_postcommit(self, context, port_assoc): pass def update_port_assoc_precommit(self, context, old_port_assoc, port_assoc): pass def update_port_assoc_postcommit(self, context, old_port_assoc, port_assoc): pass def delete_port_assoc_precommit(self, context, port_assoc): pass def delete_port_assoc_postcommit(self, context, port_assoc): pass networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/0000775000175100017510000000000013245511747031122 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/bagpipe_v2.py0000666000175100017510000002431213245511235033506 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 sqlalchemy import orm from neutron.api.rpc.callbacks import events as rpc_events from neutron.api.rpc.handlers import resources_rpc from neutron.db import api as db_api from neutron.db.models import external_net from neutron_lib.api.definitions import bgpvpn_routes_control as bgpvpn_rc_def from neutron_lib.api.definitions import bgpvpn_vni as bgpvpn_vni_def from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from oslo_log import helpers as log_helpers from oslo_log import log as logging from networking_bgpvpn.neutron.extensions import bgpvpn as bgpvpn_ext from networking_bgpvpn.neutron.services.common import utils from networking_bgpvpn.neutron.services.service_drivers import driver_api from networking_bagpipe.objects import bgpvpn as bgpvpn_objects LOG = logging.getLogger(__name__) BAGPIPE_DRIVER_NAME = "bagpipe" class BGPVPNExternalNetAssociation(n_exc.NeutronException): message = _("driver does not support associating an external" "network to a BGPVPN") @db_api.context_manager.reader def network_is_external(context, net_id): try: context.session.query(external_net.ExternalNetwork).filter_by( network_id=net_id).one() return True except orm.exc.NoResultFound: return False def _log_callback_processing_exception(resource, event, trigger, kwargs, e): LOG.exception("Error during notification processing " "%(resource)s %(event)s, %(trigger)s, " "%(kwargs)s: %(exc)s", {'trigger': trigger, 'resource': resource, 'event': event, 'kwargs': kwargs, 'exc': e}) class BaGPipeBGPVPNDriver(driver_api.BGPVPNDriverRC): """BGPVPN Service Driver class for BaGPipe""" more_supported_extension_aliases = [bgpvpn_rc_def.ALIAS, bgpvpn_vni_def.ALIAS] def __init__(self, service_plugin): super(BaGPipeBGPVPNDriver, self).__init__(service_plugin) registry.subscribe(self.registry_router_interface_created, resources.ROUTER_INTERFACE, events.AFTER_CREATE) # need to subscribe to router interface *before*_delete, # because after, we can't build the OVO objects from the DB anymore registry.subscribe(self.registry_router_interface_before_delete, resources.ROUTER_INTERFACE, events.BEFORE_DELETE) self._push_rpc = resources_rpc.ResourcesPushRpcApi() def _push_association(self, context, association, event_type): self._push_associations(context, [association], event_type) def _push_associations(self, context, associations, event_type): if not associations: return for assoc in associations: LOG.debug("pushing %s %s (%s)", event_type, assoc, assoc.bgpvpn) self._push_rpc.push(context, associations, event_type) def _common_precommit_checks(self, bgpvpn): # No support yet for specifying route distinguishers if bgpvpn.get('route_distinguishers', None): raise bgpvpn_ext.BGPVPNRDNotSupported(driver=BAGPIPE_DRIVER_NAME) def create_bgpvpn_precommit(self, context, bgpvpn): self._common_precommit_checks(bgpvpn) def delete_bgpvpn_precommit(self, context, bgpvpn): self._push_bgpvpn_associations(context, bgpvpn['id'], rpc_events.DELETED) def update_bgpvpn_precommit(self, context, old_bgpvpn, bgpvpn): self._common_precommit_checks(bgpvpn) def update_bgpvpn_postcommit(self, context, old_bgpvpn, bgpvpn): (added_keys, removed_keys, changed_keys) = ( utils.get_bgpvpn_differences(bgpvpn, old_bgpvpn)) ATTRIBUTES_TO_IGNORE = set(['name']) moving_keys = added_keys | removed_keys | changed_keys if len(moving_keys ^ ATTRIBUTES_TO_IGNORE): self._push_bgpvpn_associations(context, bgpvpn['id'], rpc_events.UPDATED) def _push_bgpvpn_associations(self, context, bgpvpn_id, event_type): self._push_associations( context, (bgpvpn_objects.BGPVPNNetAssociation.get_objects( context, bgpvpn_id=bgpvpn_id) + bgpvpn_objects.BGPVPNRouterAssociation.get_objects( context, bgpvpn_id=bgpvpn_id) ), event_type) def create_net_assoc_precommit(self, context, net_assoc): if network_is_external(context, net_assoc['network_id']): raise BGPVPNExternalNetAssociation() def create_net_assoc_postcommit(self, context, net_assoc): self._push_association( context, bgpvpn_objects.BGPVPNNetAssociation.get_object( context, id=net_assoc['id']), rpc_events.CREATED) def delete_net_assoc_precommit(self, context, net_assoc): self._push_association( context, bgpvpn_objects.BGPVPNNetAssociation.get_object( context, id=net_assoc['id']), rpc_events.DELETED) def create_port_assoc_postcommit(self, context, port_assoc): self._push_association( context, bgpvpn_objects.BGPVPNPortAssociation.get_object( context, id=port_assoc['id']), rpc_events.CREATED) def update_port_assoc_postcommit(self, context, old_port_assoc, port_assoc): self._push_association( context, bgpvpn_objects.BGPVPNPortAssociation.get_object( context, id=port_assoc['id']), rpc_events.UPDATED) def delete_port_assoc_precommit(self, context, port_assoc): self._push_association( context, bgpvpn_objects.BGPVPNPortAssociation.get_object( context, id=port_assoc['id']), rpc_events.DELETED) def create_router_assoc_postcommit(self, context, router_assoc): self._push_association( context, bgpvpn_objects.BGPVPNRouterAssociation.get_object( context, id=router_assoc['id']), rpc_events.CREATED) def delete_router_assoc_precommit(self, context, router_assoc): self._push_association( context, bgpvpn_objects.BGPVPNRouterAssociation.get_object( context, id=router_assoc['id']), rpc_events.DELETED) @log_helpers.log_method_call def notify_router_interface_created(self, context, router_id, net_id): # update associations for the networks on which the router was plugged self._push_associations( context, (bgpvpn_objects.BGPVPNNetAssociation.get_objects( context, network_id=net_id) + bgpvpn_objects.BGPVPNRouterAssociation.get_objects( context, network_id=net_id)), rpc_events.UPDATED) @log_helpers.log_method_call def notify_router_interface_deleted(self, context, router_id, net_id): # update associations for the networks on which the router was plugged associations = ( bgpvpn_objects.BGPVPNNetAssociation.get_objects( context, network_id=net_id) + bgpvpn_objects.BGPVPNRouterAssociation.get_objects( context, router_id=router_id) ) # NOTE(tmorin): the gateway_mac information in these notifications # will not be None, as it should, because unfortunately the OVObjects # are created before the DB is updated after interface removal. # So we reprocess them to empty this field... for assoc in associations: for subnet in assoc.all_subnets(net_id): subnet['gateway_mac'] = None self._push_associations(context, associations, rpc_events.UPDATED) @log_helpers.log_method_call def registry_router_interface_created(self, resource, event, trigger, **kwargs): try: context = kwargs['context'] router_id = kwargs['router_id'] net_id = kwargs['port']['network_id'] self.notify_router_interface_created(context, router_id, net_id) except Exception as e: _log_callback_processing_exception(resource, event, trigger, kwargs, e) @log_helpers.log_method_call def registry_router_interface_before_delete(self, resource, event, trigger, **kwargs): try: context = kwargs['context'] # for router_interface after_delete, in stable/newton, the # callback does not include the router_id directly, but we find # it in the port device_id router_id = kwargs['router_id'] subnet_id = kwargs['subnet_id'] # find the network for this subnet network_id = directory.get_plugin().get_subnet( context, subnet_id)['network_id'] self.notify_router_interface_deleted( context, router_id, network_id) except Exception as e: _log_callback_processing_exception(resource, event, trigger, kwargs, e) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/__init__.py0000666000175100017510000000000013245511235033213 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/bagpipe.py0000666000175100017510000005202113245511235033075 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 sqlalchemy import orm from sqlalchemy import sql from neutron.db import api as db_api from neutron.db.models import l3 from neutron.db import models_v2 from neutron.debug import debug_agent 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 const from oslo_log import helpers as log_helpers from oslo_log import log as logging from networking_bagpipe.agent.bgpvpn import rpc_client from networking_bgpvpn.neutron.db import bgpvpn_db from networking_bgpvpn.neutron.services.common import utils from networking_bgpvpn.neutron.services.service_drivers.bagpipe \ import bagpipe_v2 as v2 LOG = logging.getLogger(__name__) @log_helpers.log_method_call @db_api.context_manager.reader def get_network_info_for_port(context, port_id, network_id): """Get MAC, IP and Gateway IP addresses informations for a specific port""" try: net_info = (context.session. query(models_v2.Port.mac_address, models_v2.IPAllocation.ip_address, models_v2.Subnet.cidr, models_v2.Subnet.gateway_ip). join(models_v2.IPAllocation). join(models_v2.Subnet, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id). filter(models_v2.Subnet.ip_version == 4). filter(models_v2.Port.id == port_id).one()) (mac_address, ip_address, cidr, gateway_ip) = net_info except orm.exc.NoResultFound: return gateway_mac = ( context.session. query(models_v2.Port.mac_address). filter( models_v2.Port.network_id == network_id, (models_v2.Port.device_owner == const.DEVICE_OWNER_ROUTER_INTF) ). one_or_none() ) return {'mac_address': mac_address, 'ip_address': ip_address + cidr[cidr.index('/'):], 'gateway_ip': gateway_ip, 'gateway_mac': gateway_mac[0] if gateway_mac else None} @db_api.context_manager.reader def get_gateway_mac(context, network_id): gateway_mac = ( context.session. query(models_v2.Port.mac_address). filter( models_v2.Port.network_id == network_id, (models_v2.Port.device_owner == const.DEVICE_OWNER_ROUTER_INTF) ). one_or_none() ) return gateway_mac[0] if gateway_mac else None @db_api.context_manager.reader def get_network_ports(context, network_id): # NOTE(tmorin): currents callers don't look at detailed results # but only test if at least one result exist => can be optimized # by returning a count, rather than all port information return (context.session.query(models_v2.Port). filter(models_v2.Port.network_id == network_id, models_v2.Port.admin_state_up == sql.true()).all()) @db_api.context_manager.reader def get_router_ports(context, router_id): return ( context.session.query(models_v2.Port). filter( models_v2.Port.device_id == router_id, models_v2.Port.device_owner == const.DEVICE_OWNER_ROUTER_INTF ).all() ) @db_api.context_manager.reader def get_router_bgpvpn_assocs(context, router_id): return ( context.session.query(bgpvpn_db.BGPVPNRouterAssociation). filter( bgpvpn_db.BGPVPNRouterAssociation.router_id == router_id ).all() ) @db_api.context_manager.reader def get_network_bgpvpn_assocs(context, net_id): return ( context.session.query(bgpvpn_db.BGPVPNNetAssociation). filter( bgpvpn_db.BGPVPNNetAssociation.network_id == net_id ).all() ) @db_api.context_manager.reader def get_bgpvpns_of_router_assocs_by_network(context, net_id): return ( context.session.query(bgpvpn_db.BGPVPN). join(bgpvpn_db.BGPVPN.router_associations). join(bgpvpn_db.BGPVPNRouterAssociation.router). join(l3.Router.attached_ports). join(l3.RouterPort.port). filter( models_v2.Port.network_id == net_id ).all() ) @db_api.context_manager.reader def get_networks_for_router(context, router_id): ports = get_router_ports(context, router_id) if ports: return set([port['network_id'] for port in ports]) else: return [] def _log_callback_processing_exception(resource, event, trigger, kwargs, e): LOG.exception("Error during notification processing " "%(resource)s %(event)s, %(trigger)s, " "%(kwargs)s: %(exc)s", {'trigger': trigger, 'resource': resource, 'event': event, 'kwargs': kwargs, 'exc': e}) class BaGPipeBGPVPNDriver(v2.BaGPipeBGPVPNDriver): """BGPVPN Service Driver class for BaGPipe""" def __init__(self, service_plugin): super(BaGPipeBGPVPNDriver, self).__init__(service_plugin) self.agent_rpc = rpc_client.BGPVPNAgentNotifyApi() registry.subscribe(self.registry_port_updated, resources.PORT, events.AFTER_UPDATE) registry.subscribe(self.registry_port_deleted, resources.PORT, events.AFTER_DELETE) registry.subscribe(self.registry_router_interface_deleted, resources.ROUTER_INTERFACE, events.AFTER_DELETE) def _format_bgpvpn(self, context, bgpvpn, network_id): """JSON-format BGPVPN BGPVPN, network identifiers, and route targets. """ formatted_bgpvpn = {'id': bgpvpn['id'], 'network_id': network_id, 'gateway_mac': get_gateway_mac(context, network_id)} formatted_bgpvpn.update( self._format_bgpvpn_network_route_targets([bgpvpn])) return formatted_bgpvpn def _format_bgpvpn_network_route_targets(self, bgpvpns): """Format BGPVPN network informations (VPN type and route targets) [{ 'type': 'l3', 'route_targets': ['12345:1', '12345:2'], 'import_targets': ['12345:3'], 'export_targets': ['12345:4'] }, { 'type': 'l3', 'route_targets': ['12346:1'] }, { 'type': 'l2', 'route_targets': ['12347:1'] } ] to { 'l3vpn' : { 'import_rt': ['12345:1', '12345:2', '12345:3', '12346:1'], 'export_rt': ['12345:1', '12345:2', '12345:4', '12346:1'] }, 'l2vpn' : { 'import_rt': ['12347:1'], 'export_rt': ['12347:1'] } } """ bgpvpn_rts = {} for bgpvpn in bgpvpns: # Add necessary keys to BGP VPN route targets dictionary if bgpvpn['type'] + 'vpn' not in bgpvpn_rts: bgpvpn_rts.update( {bgpvpn['type'] + 'vpn': {'import_rt': [], 'export_rt': []}} ) if 'route_targets' in bgpvpn: bgpvpn_rts[bgpvpn['type'] + 'vpn']['import_rt'] += ( bgpvpn['route_targets'] ) bgpvpn_rts[bgpvpn['type'] + 'vpn']['export_rt'] += ( bgpvpn['route_targets'] ) if 'import_targets' in bgpvpn: bgpvpn_rts[bgpvpn['type'] + 'vpn']['import_rt'] += ( bgpvpn['import_targets'] ) if 'export_targets' in bgpvpn: bgpvpn_rts[bgpvpn['type'] + 'vpn']['export_rt'] += ( bgpvpn['export_targets'] ) for attribute in ('import_rt', 'export_rt'): if bgpvpn_rts[bgpvpn['type'] + 'vpn'][attribute]: bgpvpn_rts[bgpvpn['type'] + 'vpn'][attribute] = list( set(bgpvpn_rts[bgpvpn['type'] + 'vpn'][attribute])) return bgpvpn_rts def _bgpvpns_for_network(self, context, network_id): return ( self.bgpvpn_db.get_bgpvpns( context, filters={ 'networks': [network_id], }, ) or self.retrieve_bgpvpns_of_router_assocs_by_network(context, network_id) ) def _networks_for_bgpvpn(self, context, bgpvpn): networks = [] networks.extend(bgpvpn['networks']) for router_id in bgpvpn['routers']: networks.extend(get_networks_for_router(context, router_id)) return list(set(networks)) def _retrieve_bgpvpn_network_info_for_port(self, context, port): """Retrieve BGP VPN network informations for a specific port { 'network_id': , 'mac_address': '00:00:de:ad:be:ef', 'ip_address': '10.0.0.2', 'gateway_ip': '10.0.0.1', 'gateway_mac': 'aa:bb:cc:dd:ee:ff', # if a router interface exists 'l3vpn' : { 'import_rt': ['12345:1', '12345:2', '12345:3'], 'export_rt': ['12345:1', '12345:2', '12345:4'] } } """ port_id = port['id'] network_id = port['network_id'] bgpvpn_network_info = {} bgpvpns = self._bgpvpns_for_network(context, network_id) # NOTE(tmorin): We currently need to send 'network_id', 'mac_address', # 'ip_address', 'gateway_ip' to the agent, even in the absence of # a BGPVPN bound to the port. If we don't this information will # lack on an update_bgpvpn RPC. When the agent will have the ability # to retrieve this info by itself, we'll change this method # to return {} if there is no bound bgpvpn. bgpvpn_rts = self._format_bgpvpn_network_route_targets(bgpvpns) LOG.debug("Port connected on BGPVPN network %s with route targets " "%s" % (network_id, bgpvpn_rts)) bgpvpn_network_info.update(bgpvpn_rts) LOG.debug("Getting port %s network details" % port_id) network_info = get_network_info_for_port(context, port_id, network_id) if not network_info: LOG.warning("No network information for net %s", network_id) return bgpvpn_network_info.update(network_info) return bgpvpn_network_info @db_api.context_manager.reader def retrieve_bgpvpns_of_router_assocs_by_network(self, context, network_id): return [self.bgpvpn_db._make_bgpvpn_dict(bgpvpn) for bgpvpn in get_bgpvpns_of_router_assocs_by_network(context, network_id)] def delete_bgpvpn_postcommit(self, context, bgpvpn): for net_id in self._networks_for_bgpvpn(context, bgpvpn): if get_network_ports(context, net_id): # Format BGPVPN before sending notification self.agent_rpc.delete_bgpvpn( context, self._format_bgpvpn(context, bgpvpn, net_id)) def update_bgpvpn_postcommit(self, context, old_bgpvpn, bgpvpn): super(BaGPipeBGPVPNDriver, self).update_bgpvpn_postcommit( context, old_bgpvpn, bgpvpn) (added_keys, removed_keys, changed_keys) = ( utils.get_bgpvpn_differences(bgpvpn, old_bgpvpn)) ATTRIBUTES_TO_IGNORE = set('name') moving_keys = added_keys | removed_keys | changed_keys if len(moving_keys ^ ATTRIBUTES_TO_IGNORE): for net_id in self._networks_for_bgpvpn(context, bgpvpn): if (get_network_ports(context, net_id)): self._update_bgpvpn_for_network(context, net_id, bgpvpn) def _update_bgpvpn_for_net_with_id(self, context, network_id, bgpvpn_id): if get_network_ports(context, network_id): bgpvpn = self.get_bgpvpn(context, bgpvpn_id) self._update_bgpvpn_for_network(context, network_id, bgpvpn) def _update_bgpvpn_for_network(self, context, net_id, bgpvpn): formated_bgpvpn = self._format_bgpvpn(context, bgpvpn, net_id) self.agent_rpc.update_bgpvpn(context, formated_bgpvpn) def create_net_assoc_postcommit(self, context, net_assoc): super(BaGPipeBGPVPNDriver, self).create_net_assoc_postcommit(context, net_assoc) self._update_bgpvpn_for_net_with_id(context, net_assoc['network_id'], net_assoc['bgpvpn_id']) def delete_net_assoc_postcommit(self, context, net_assoc): if get_network_ports(context, net_assoc['network_id']): bgpvpn = self.get_bgpvpn(context, net_assoc['bgpvpn_id']) formated_bgpvpn = self._format_bgpvpn(context, bgpvpn, net_assoc['network_id']) self.agent_rpc.delete_bgpvpn(context, formated_bgpvpn) def _ignore_port(self, context, port): if (port['device_owner'].startswith(const.DEVICE_OWNER_NETWORK_PREFIX) and not port['device_owner'] in (debug_agent.DEVICE_OWNER_COMPUTE_PROBE, debug_agent.DEVICE_OWNER_NETWORK_PROBE)): LOG.info("Port %s owner is network:*, we'll do nothing", port['id']) return True if v2.network_is_external(context, port['network_id']): LOG.info("Port %s is on an external network, we'll do nothing", port['id']) return True return False @log_helpers.log_method_call def notify_port_updated(self, context, port, original_port): if self._ignore_port(context, port): return agent_host = port[portbindings.HOST_ID] port_bgpvpn_info = {'id': port['id'], 'network_id': port['network_id']} if (port['status'] == const.PORT_STATUS_ACTIVE and original_port['status'] != const.PORT_STATUS_ACTIVE): LOG.debug("notify_port_updated, port became ACTIVE") bgpvpn_network_info = ( self._retrieve_bgpvpn_network_info_for_port(context, port) ) if bgpvpn_network_info: port_bgpvpn_info.update(bgpvpn_network_info) self.agent_rpc.attach_port_on_bgpvpn(context, port_bgpvpn_info, agent_host) else: # currently not reached, because we need # _retrieve_bgpvpn_network_info_for_port to always # return network information, even in the absence # of any BGPVPN port bound. pass elif (port['status'] == const.PORT_STATUS_DOWN and original_port['status'] != const.PORT_STATUS_DOWN): LOG.debug("notify_port_updated, port became DOWN") self.agent_rpc.detach_port_from_bgpvpn(context, port_bgpvpn_info, agent_host) else: LOG.debug("new port status is %s, origin status was %s," " => no action", port['status'], original_port['status']) @log_helpers.log_method_call def notify_port_deleted(self, context, port): port_bgpvpn_info = {'id': port['id'], 'network_id': port['network_id']} if self._ignore_port(context, port): return self.agent_rpc.detach_port_from_bgpvpn(context, port_bgpvpn_info, port[portbindings.HOST_ID]) def create_router_assoc_postcommit(self, context, router_assoc): super(BaGPipeBGPVPNDriver, self).create_router_assoc_postcommit( context, router_assoc) for net_id in get_networks_for_router(context, router_assoc['router_id']): self._update_bgpvpn_for_net_with_id(context, net_id, router_assoc['bgpvpn_id']) def delete_router_assoc_postcommit(self, context, router_assoc): for net_id in get_networks_for_router(context, router_assoc['router_id']): net_assoc = {'network_id': net_id, 'bgpvpn_id': router_assoc['bgpvpn_id']} self.delete_net_assoc_postcommit(context, net_assoc) @log_helpers.log_method_call def notify_router_interface_created(self, context, router_id, net_id): super(BaGPipeBGPVPNDriver, self).notify_router_interface_created( context, router_id, net_id) net_assocs = get_network_bgpvpn_assocs(context, net_id) router_assocs = get_router_bgpvpn_assocs(context, router_id) # if this router_interface is on a network bound to a BGPVPN, # or if this router is bound to a BGPVPN, # then we need to send and update for this network, including # the gateway_mac if net_assocs or router_assocs: for bgpvpn in self._bgpvpns_for_network(context, net_id): self._update_bgpvpn_for_network(context, net_id, bgpvpn) for router_assoc in router_assocs: self._update_bgpvpn_for_net_with_id(context, net_id, router_assoc['bgpvpn_id']) @log_helpers.log_method_call def notify_router_interface_deleted(self, context, router_id, net_id): super(BaGPipeBGPVPNDriver, self).notify_router_interface_deleted( context, router_id, net_id) net_assocs = get_network_bgpvpn_assocs(context, net_id) router_assocs = get_router_bgpvpn_assocs(context, router_id) if net_assocs or router_assocs: for bgpvpn in self._bgpvpns_for_network(context, net_id): self._update_bgpvpn_for_network(context, net_id, bgpvpn) for router_assoc in router_assocs: net_assoc = {'network_id': net_id, 'bgpvpn_id': router_assoc['bgpvpn_id']} self.delete_net_assoc_postcommit(context, net_assoc) @log_helpers.log_method_call def registry_port_updated(self, resource, event, trigger, **kwargs): try: context = kwargs['context'] port = kwargs['port'] original_port = kwargs['original_port'] self.notify_port_updated(context, port, original_port) except Exception as e: _log_callback_processing_exception(resource, event, trigger, kwargs, e) @log_helpers.log_method_call def registry_port_deleted(self, resource, event, trigger, **kwargs): try: context = kwargs['context'] port = kwargs['port'] self.notify_port_deleted(context, port) except Exception as e: _log_callback_processing_exception(resource, event, trigger, kwargs, e) @log_helpers.log_method_call def registry_router_interface_before_delete(self, resource, event, trigger, **kwargs): # we don't do anything for this event, contrarily to parent class pass @log_helpers.log_method_call def registry_router_interface_deleted(self, resource, event, trigger, **kwargs): try: context = kwargs['context'] # for router_interface after_delete, in stable/newton, the # callback does not include the router_id directly, but we find # it in the port device_id router_id = kwargs['port']['device_id'] net_id = kwargs['port']['network_id'] self.notify_router_interface_deleted(context, router_id, net_id) except Exception as e: _log_callback_processing_exception(resource, event, trigger, kwargs, e) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/0000775000175100017510000000000013245511747032210 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/opencontrail.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/opencontrail0000666000175100017510000004466613245511235034642 0ustar zuulzuul00000000000000# Copyright (c) 2015 Cloudwatt. # 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 uuid from debtcollector import removals from oslo_log import log from oslo_serialization import jsonutils from oslo_utils import uuidutils from neutron_lib import exceptions as n_exc from networking_bgpvpn.neutron.extensions import bgpvpn as bgpvpn_ext from networking_bgpvpn.neutron.services.common import constants from networking_bgpvpn.neutron.services.common import utils from networking_bgpvpn.neutron.services.service_drivers import driver_api from networking_bgpvpn.neutron.services.service_drivers.opencontrail import \ exceptions as oc_exc from networking_bgpvpn.neutron.services.service_drivers.opencontrail import \ opencontrail_client OPENCONTRAIL_BGPVPN_DRIVER_NAME = 'OpenContrail' LOG = log.getLogger(__name__) MESSAGE = ("replaced by a new driver which could be found in the Contrail " "Neutron monolithic core plugin tree: 'neutron_plugin_contrail." "plugins.opencontrail.networking_bgpvpn.contrail." "ContrailBGPVPNDriver'") @removals.removed_class('OpenContrailBGPVPNDriver', version='Queens', removal_version='Rocky', message=MESSAGE) class OpenContrailBGPVPNDriver(driver_api.BGPVPNDriverBase): """BGP VPN Service Driver class for OpenContrail.""" def __init__(self, service_plugin): super(OpenContrailBGPVPNDriver, self).__init__(service_plugin) LOG.debug("OpenContrailBGPVPNDriver service_plugin : %s", service_plugin) def _get_opencontrail_api_client(self, context): return opencontrail_client.OpenContrailAPIBaseClient() def _locate_rt(self, oc_client, rt_fq_name): try: rt_uuid = oc_client.fqname_to_id('route-target', rt_fq_name) except oc_exc.OpenContrailAPINotFound: body = { 'route-target': { "fq_name": [':'.join(rt_fq_name)] } } rt_uuid = oc_client.create('Route Target', body)['uuid'] return rt_uuid def _clean_route_targets(self, oc_client, rts_fq_name): for rt_fq_name in rts_fq_name: try: rt_uuid = oc_client.fqname_to_id('route-target', rt_fq_name) except oc_exc.OpenContrailAPINotFound: continue rt = oc_client.show('Route Target', rt_uuid) if 'routing_instance_back_refs' not in rt.keys(): # rt not use anymore, remove it rt = oc_client.remove('Route Target', rt_uuid) def _update_rt_ri_association(self, oc_client, operation, ri_id, rt_fq_name, import_export=None): rt_uuid = self._locate_rt(oc_client, rt_fq_name) kwargs = { "operation": operation, "resource_type": "routing-instance", "resource_uuid": ri_id, "ref_type": "route-target", "ref_fq_name": rt_fq_name, "ref_uuid": rt_uuid, "attributes": { "import_export": import_export } } oc_client.ref_update(**kwargs) if operation == 'DELETE': self._clean_route_targets(oc_client, [rt_fq_name]) def _get_ri_id_of_network(self, oc_client, network_id): try: network = oc_client.show('Virtual Network', network_id) ri_fq_name = network['fq_name'] + [network['fq_name'][-1]] for ri_ref in network.get('routing_instances', []): if ri_ref['to'] == ri_fq_name: return ri_ref['uuid'] except (oc_exc.OpenContrailAPINotFound, IndexError): raise n_exc.NetworkNotFound(net_id=network_id) raise n_exc.NetworkNotFound(net_id=network_id) def _set_bgpvpn_association(self, oc_client, operation, bgpvpn, networks=None): if networks is None: networks = [] for network_id in networks: try: net_ri_id = self._get_ri_id_of_network(oc_client, network_id) except n_exc.NetworkNotFound: LOG.info("Network %s not found, cleanup route targets", network_id) rts_fq_name = (bgpvpn['route_targets'] + bgpvpn['import_targets'] + bgpvpn['export_targets']) rts_fq_name = [['target'] + rt.split(':') for rt in rts_fq_name] self._clean_route_targets(oc_client, rts_fq_name) return bgpvpn if bgpvpn['type'] == constants.BGPVPN_L3: for rt in bgpvpn['route_targets']: rt_fq_name = ['target'] + rt.split(':') self._update_rt_ri_association(oc_client, operation, net_ri_id, rt_fq_name) for rt in bgpvpn['import_targets']: rt_fq_name = ['target'] + rt.split(':') self._update_rt_ri_association(oc_client, operation, net_ri_id, rt_fq_name, import_export="import") for rt in bgpvpn['export_targets']: rt_fq_name = ['target'] + rt.split(':') self._update_rt_ri_association(oc_client, operation, net_ri_id, rt_fq_name, import_export="export") return bgpvpn # Check if tenant ID exists by reading it from the Contrail API; # if not, it sends an exception def _check_tenant_id(self, oc_client, tenant_id): try: tenant_id = str(uuid.UUID(tenant_id)) except ValueError: raise oc_exc.OpenContrailMalformedUUID(uuid=tenant_id) oc_client.show('Project', tenant_id) def create_bgpvpn(self, context, bgpvpn): LOG.debug("create_bgpvpn_ called with %s" % bgpvpn) # Only support l3 technique if not bgpvpn['type']: bgpvpn['type'] = constants.BGPVPN_L3 elif bgpvpn['type'] != constants.BGPVPN_L3: raise bgpvpn_ext.BGPVPNTypeNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME, type=bgpvpn['type']) # Does not support to set route distinguisher if 'route_distinguishers' in bgpvpn and bgpvpn['route_distinguishers']: raise bgpvpn_ext.BGPVPNRDNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME) oc_client = self._get_opencontrail_api_client(context) # Check if tenant ID exists; # if not, it sends an exception self._check_tenant_id(oc_client, bgpvpn['tenant_id']) bgpvpn['id'] = uuidutils.generate_uuid() oc_client.kv_store('STORE', key=bgpvpn['id'], value={'bgpvpn': bgpvpn}) return utils.make_bgpvpn_dict(bgpvpn) def get_bgpvpns(self, context, filters=None, fields=None): LOG.debug("get_bgpvpns called, fields = %s, filters = %s" % (fields, filters)) oc_client = self._get_opencontrail_api_client(context) bgpvpns = [] for kv_dict in oc_client.kv_store('RETRIEVE'): try: value = jsonutils.loads(kv_dict['value']) except ValueError: continue if (isinstance(value, dict) and 'bgpvpn' in value and utils.filter_resource(value['bgpvpn'], filters)): bgpvpn = value['bgpvpn'] if not fields or 'networks' in fields: bgpvpn['networks'] = \ [net_assoc['network_id'] for net_assoc in self.get_net_assocs(context, bgpvpn['id'])] bgpvpns.append(utils.make_bgpvpn_dict(bgpvpn, fields)) return bgpvpns def _clean_bgpvpn_assoc(self, oc_client, bgpvpn_id): for kv_dict in oc_client.kv_store('RETRIEVE'): try: value = jsonutils.loads(kv_dict['value']) except ValueError: continue if (isinstance(value, dict) and 'bgpvpn_net_assoc' in value and value['bgpvpn_net_assoc']['bgpvpn_id'] == bgpvpn_id): assoc_id = value['bgpvpn_net_assoc']['id'] oc_client.kv_store('DELETE', key=assoc_id) def get_bgpvpn(self, context, id, fields=None): LOG.debug("get_bgpvpn called for id %s with fields = %s" % (id, fields)) oc_client = self._get_opencontrail_api_client(context) try: bgpvpn = jsonutils.loads(oc_client.kv_store('RETRIEVE', key=id)) except (oc_exc.OpenContrailAPINotFound, ValueError): raise bgpvpn_ext.BGPVPNNotFound(id=id) if (not isinstance(bgpvpn, dict) or 'bgpvpn' not in bgpvpn): raise bgpvpn_ext.BGPVPNNotFound(id=id) bgpvpn = bgpvpn['bgpvpn'] if not fields or 'networks' in fields: bgpvpn['networks'] = [net_assoc['network_id'] for net_assoc in self.get_net_assocs(context, id)] return utils.make_bgpvpn_dict(bgpvpn, fields) def update_bgpvpn(self, context, id, new_bgpvpn): LOG.debug("update_bgpvpn called with %s for %s" % (new_bgpvpn, id)) oc_client = self._get_opencontrail_api_client(context) old_bgpvpn = self.get_bgpvpn(context, id) networks = old_bgpvpn.get('networks', []) bgpvpn = old_bgpvpn.copy() bgpvpn.update(new_bgpvpn) (added_keys, removed_keys, changed_keys) = \ utils.get_bgpvpn_differences(bgpvpn, old_bgpvpn) if not (added_keys or removed_keys or changed_keys): return utils.make_bgpvpn_dict(bgpvpn) # Does not support to update route distinguisher if 'route_distinguishers' in added_keys | removed_keys | changed_keys: raise bgpvpn_ext.BGPVPNRDNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME) rt_keys = set(['route_targets', 'import_targets', 'export_targets']) if (rt_keys & added_keys or rt_keys & changed_keys or rt_keys & removed_keys): self._set_bgpvpn_association(oc_client, 'DELETE', old_bgpvpn, networks) self._set_bgpvpn_association(oc_client, 'ADD', bgpvpn, networks) oc_client.kv_store('STORE', key=id, value={'bgpvpn': bgpvpn}) return utils.make_bgpvpn_dict(bgpvpn) def delete_bgpvpn(self, context, id): LOG.debug("delete_bgpvpn called for id %s" % id) bgpvpn = self.get_bgpvpn(context, id) networks = bgpvpn.get('networks', []) oc_client = self._get_opencontrail_api_client(context) self._set_bgpvpn_association(oc_client, 'DELETE', bgpvpn, networks) self._clean_bgpvpn_assoc(oc_client, id) oc_client.kv_store('DELETE', key=id) def create_net_assoc(self, context, bgpvpn_id, network_association): LOG.debug("create_net_assoc called for bgpvpn %s with network %s" % (bgpvpn_id, network_association['network_id'])) bgpvpn = self.get_bgpvpn(context, bgpvpn_id) oc_client = self._get_opencontrail_api_client(context) network_id = network_association['network_id'] if network_id not in bgpvpn.get('networks', []): assoc_uuid = uuidutils.generate_uuid() self._set_bgpvpn_association(oc_client, 'ADD', bgpvpn, [network_id]) assoc_dict = utils.make_net_assoc_dict( assoc_uuid, network_association['tenant_id'], bgpvpn_id, network_association['network_id']) oc_client.kv_store('STORE', key=assoc_uuid, value={'bgpvpn_net_assoc': assoc_dict}) return assoc_dict else: # the tuple (bgpvpn_id, network_id) is necessarily unique return self.get_net_assocs(context, bgpvpn_id, filters={'network_id': network_id})[0] def get_net_assoc(self, context, assoc_id, bgpvpn_id, fields=None): LOG.debug("get_net_assoc called for %s for BGPVPN %s, with fields = %s" % (assoc_id, bgpvpn_id, fields)) oc_client = self._get_opencontrail_api_client(context) try: net_assoc = jsonutils.loads( oc_client.kv_store('RETRIEVE', key=assoc_id)) except (oc_exc.OpenContrailAPINotFound, ValueError): raise bgpvpn_ext.BGPVPNNetAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) if (not isinstance(net_assoc, dict) or 'bgpvpn_net_assoc' not in net_assoc): raise bgpvpn_ext.BGPVPNNetAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) net_assoc = net_assoc['bgpvpn_net_assoc'] if net_assoc['bgpvpn_id'] != bgpvpn_id: raise bgpvpn_ext.BGPVPNNetAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) # It the bgpvpn was deleted, the 'get_bgpvpn' will clean all related # associations and replaces BGPVPNNotFound by a BGPVPNNetAssocNotFound try: get_fields = ['tenant_id', 'route_targets', 'import_targets', 'export_targets'] bgpvpn = self.get_bgpvpn(context, net_assoc['bgpvpn_id'], fields=get_fields) except bgpvpn_ext.BGPVPNNotFound: raise bgpvpn_ext.BGPVPNNetAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) # If the network was delete all bgpvpn related association should be # deleted also try: oc_client.id_to_fqname(net_assoc['network_id']) except oc_exc.OpenContrailAPINotFound: self._set_bgpvpn_association(oc_client, 'DELETE', bgpvpn, [net_assoc['network_id']]) oc_client.kv_store('DELETE', key=assoc_id) raise bgpvpn_ext.BGPVPNNetAssocNotFound(id=assoc_id, bgpvpn_id=bgpvpn_id) net_assoc = utils.make_net_assoc_dict(net_assoc['id'], net_assoc['tenant_id'], net_assoc['bgpvpn_id'], net_assoc['network_id'], fields) return net_assoc def get_net_assocs(self, context, bgpvpn_id, filters=None, fields=None): LOG.debug("get_net_assocs called for bgpvpn %s, fields = %s, " "filters = %s" % (bgpvpn_id, fields, filters)) oc_client = self._get_opencontrail_api_client(context) get_fields = ['tenant_id', 'route_targets', 'import_targets', 'export_targets'] bgpvpn = self.get_bgpvpn(context, bgpvpn_id, fields=get_fields) bgpvpn_net_assocs = [] for kv_dict in oc_client.kv_store('RETRIEVE'): try: value = jsonutils.loads(kv_dict['value']) except ValueError: continue if (isinstance(value, dict) and 'bgpvpn_net_assoc' in value and utils.filter_resource(value['bgpvpn_net_assoc'], filters) and value['bgpvpn_net_assoc']['bgpvpn_id'] == bgpvpn_id): net_assoc = value['bgpvpn_net_assoc'] # If the network was delete all bgpvpn related association # should be deleted also try: oc_client.id_to_fqname(net_assoc['network_id']) except oc_exc.OpenContrailAPINotFound: self._set_bgpvpn_association(oc_client, 'DELETE', bgpvpn, [net_assoc['network_id']]) oc_client.kv_store('DELETE', key=net_assoc['id']) continue net_assoc = utils.make_net_assoc_dict(net_assoc['id'], net_assoc['tenant_id'], net_assoc['bgpvpn_id'], net_assoc['network_id'], fields) bgpvpn_net_assocs.append(net_assoc) return bgpvpn_net_assocs def delete_net_assoc(self, context, assoc_id, bgpvpn_id): LOG.debug("delete_net_assoc called for %s" % assoc_id) net_assoc = self.get_net_assoc(context, assoc_id, bgpvpn_id) fields = ['type', 'route_targets', 'import_targets', 'export_targets'] bgpvpn = self.get_bgpvpn(context, net_assoc['bgpvpn_id'], fields=fields) oc_client = self._get_opencontrail_api_client(context) self._set_bgpvpn_association(oc_client, 'DELETE', bgpvpn, [net_assoc['network_id']]) oc_client.kv_store('DELETE', key=assoc_id) return net_assoc def create_router_assoc(self, context, bgpvpn_id, router_association): raise bgpvpn_ext.BGPVPNRouterAssociationNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME) def get_router_assoc(self, context, assoc_id, bgpvpn_id, fields=None): raise bgpvpn_ext.BGPVPNRouterAssociationNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME) def get_router_assocs(self, context, bgpvpn_id, filters=None, fields=None): raise bgpvpn_ext.BGPVPNRouterAssociationNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME) def delete_router_assoc(self, context, assoc_id, bgpvpn_id): raise bgpvpn_ext.BGPVPNRouterAssociationNotSupported( driver=OPENCONTRAIL_BGPVPN_DRIVER_NAME) ././@LongLink0000000000000000000000000000014600000000000011216 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/exceptions.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/exceptions.p0000666000175100017510000000374713245511235034557 0ustar zuulzuul00000000000000# Copyright (C) 2015 Cloudwatt # 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 exceptions as n_exc from networking_bgpvpn._i18n import _ # OpenContrail client API exceptions class OpenContrailAPIFailed(n_exc.NeutronException): message = _("Could not reach OpenContrail API server : %(url)s " "Exception: %(excption)s.") class OpenContrailAPIError(n_exc.NeutronException): message = _('OpenContrail API returned %(status)s %(reason)s') class OpenContrailAPINotSupported(n_exc.BadRequest): message = _('OpenContrail API client cannot %(action)s on %(resource)s') class OpenContrailAPIBadFqName(n_exc.BadRequest): message = _("Bad fq_name for forming a fq_name to ID request") class OpenContrailAPIBadUUID(n_exc.BadRequest): message = _("Bad UUID for forming a UUID to fq_name request") class OpenContrailAPIBadKVAttributes(n_exc.BadRequest): message = _("Bad attributes for forming a key/value store request") class OpenContrailAPINotAuthorized(n_exc.NotAuthorized): pass class OpenContrailAPINotFound(n_exc.NotFound): message = _("%(resource)s %(id)s does not exist") class OpenContrailAPIConflict(n_exc.Conflict): message = _("OpenContrail API conflict: %(reason)s") class OpenContrailAPIBadRequest(n_exc.BadRequest): message = _("OpenContrail API bad request: %(reason)s") class OpenContrailMalformedUUID(n_exc.BadRequest): message = _("Malformed UUID: %(uuid)s") networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/__init__.py0000666000175100017510000000000013245511235034301 0ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/opencontrail_client.pynetworking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/opencontrail/opencontrail0000666000175100017510000003024313245511235034624 0ustar zuulzuul00000000000000# Copyright (C) 2014 eNovance SAS # 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 networking_bgpvpn._i18n import _ from networking_bgpvpn.neutron.services.service_drivers.opencontrail import \ exceptions as oc_exc from oslo_config import cfg from oslo_log import log from oslo_serialization import jsonutils from oslo_utils import uuidutils import requests from six.moves import http_client as httplib from six.moves.urllib import parse as urlparse LOG = log.getLogger(__name__) CONF = cfg.CONF opencontrail_opts = [ cfg.IntOpt('request_timeout', default=30, help='Timeout seconds for HTTP requests. Set it to None to ' 'disable timeout.'), ] CONF.register_opts(opencontrail_opts, 'APISERVER') def get_auth_token(): DEFAULT_HEADERS = { 'Content-type': 'application/json; charset="UTF-8"', 'X-Contrail-Useragent': 'bgppn_opencontrail_client', } admin_user = cfg.CONF.keystone_authtoken.admin_user admin_password = cfg.CONF.keystone_authtoken.admin_password admin_tenant_name = cfg.CONF.keystone_authtoken.admin_tenant_name auth_body = { "auth": { "passwordCredentials": { "username": admin_user, "password": admin_password }, "tenantName": admin_tenant_name } } try: auth_type = CONF.keystone_authtoken.auth_type except cfg.NoSuchOptError: auth_type = "keystone" if auth_type != "keystone": return try: auth_url = cfg.CONF.keystone_authtoken.auth_url except cfg.NoSuchOptError: try: auth_host = cfg.CONF.keystone_authtoken.auth_host except cfg.NoSuchOptError: auth_host = "127.0.0.1" try: auth_protocol = cfg.CONF.keystone_authtoken.auth_protocol except cfg.NoSuchOptError: auth_protocol = "http" try: auth_port = cfg.CONF.keystone_authtoken.auth_port except cfg.NoSuchOptError: auth_port = "35357" auth_url = "%s://%s:%s" % (auth_protocol, auth_host, auth_port) url = auth_url + '/v2.0/tokens' response = requests.post(url, data=jsonutils.dumps(auth_body), headers=DEFAULT_HEADERS) if response.status_code == 200: auth_content = jsonutils.loads(response.text) return auth_content['access']['token']['id'] else: raise RuntimeError(_("Authentication failure. Code: %(code)d, " "reason: %(reason)s") % {'code': response.status_code, 'reason': response.reason}) class RequestHandler(object): """Handles processing requests.""" def __init__(self): self._host = CONF.APISERVER.api_server_ip self._port = CONF.APISERVER.api_server_port self._request_timeout = float(CONF.APISERVER.request_timeout) self._api_url = 'http://' + self._host + ':' + str(self._port) self._token = get_auth_token() self._pool = requests.Session() self._resource = "" self._id = "" def delete(self, url, body=None, headers=None, params=None): return self._do_request("DELETE", url, body=body, headers=headers, params=params) def get(self, url, body=None, headers=None, params=None): return self._do_request("GET", url, body=body, headers=headers, params=params) def post(self, url, body=None, headers=None, params=None): return self._do_request("POST", url, body=body, headers=headers, params=params) def put(self, url, body=None, headers=None, params=None): return self._do_request("PUT", url, body=body, headers=headers, params=params) def _do_request(self, method, url, body=None, headers=None, params=None, retry_auth=True): req_params = self._get_req_params(data=body) if headers: req_params['headers'].update(headers) url = urlparse.urljoin(self._api_url, url) if params and isinstance(params, dict): url += '?' + urlparse.urlencode(params, doseq=1) if url[-1] == '/': url = url[:-1] self._log_req(method, url, params, req_params) try: response = self._pool.request(method, url, **req_params) except Exception as e: raise oc_exc.OpenContrailAPIFailed(url=url, excption=e) self._log_res(response) if response.status_code == httplib.UNAUTHORIZED and retry_auth: self._auth_token = get_auth_token() return self._do_request(method, url, body=body, headers=headers, params=params, retry_auth=False) if response.status_code == httplib.UNAUTHORIZED and not retry_auth: raise oc_exc.OpenContrailAPINotAuthorized if response.status_code == httplib.NOT_FOUND: raise oc_exc.OpenContrailAPINotFound(resource=self._resource, id=self._id) if response.status_code == httplib.CONFLICT: raise oc_exc.OpenContrailAPIConflict(reason=response.reason) if response.status_code == httplib.BAD_REQUEST: raise oc_exc.OpenContrailAPIBadRequest(reason=response.reason) if response.status_code is not httplib.OK: raise oc_exc.OpenContrailAPIError(status=response.status_code, reason=response.reason) if response.content: return response.json() def _get_req_params(self, data=None): req_params = { 'headers': { 'Content-type': 'application/json; charset="UTF-8"', 'Accept': "application/json", 'X-Auth-Token': self._token, }, 'allow_redirects': False, 'timeout': self._request_timeout, } if data: req_params.update({'data': jsonutils.dumps(data)}) return req_params @staticmethod def _log_req(method, url, params, req_params): if not CONF.debug: return curl_command = ['REQ: curl -i -X %s ' % method] if params and isinstance(params, dict): url += '?' + urlparse.urlencode(params, doseq=1) curl_command.append('"%s" ' % url) for name, value in req_params['headers'].items(): curl_command.append('-H "%s: %s" ' % (name, value)) if ('data' in req_params.keys() and (isinstance(req_params['data'], dict) or isinstance(req_params['data'], str))): curl_command.append("-d '%s'" % (req_params['data'])) LOG.debug(''.join(curl_command)) @staticmethod def _log_res(resp): if CONF.debug: dump = ['RES: \n', 'HTTP %.1f %s %s\n' % (resp.raw.version, resp.status_code, resp.reason)] dump.extend('%s: %s\n' % (k, v) for k, v in (resp.headers).items()) dump.append('\n') if resp.content: dump.extend([resp.content, '\n']) LOG.debug(''.join(dump)) class OpenContrailAPIBaseClient(RequestHandler): """OpenContrail Base REST API Client.""" resource_path = { 'FQName to ID': '/fqname-to-id/', 'ID to FQName': '/id-to-fqname/', 'Ref Update': "/ref-update/", 'Virtual Network': '/virtual-networks/', 'Routing Instance': '/routing-instances/', 'Route Target': '/route-targets/', 'Key Value Store': '/useragent-kv/', 'Project': '/projects/' } def list(self, resource, **params): """Fetches a list of resources.""" res = self.resource_path.get(resource, None) if not res: raise oc_exc.OpenContrailAPINotSupported(action='list', resource=resource) self._resource = resource return self.get(res, params=params) def show(self, resource, id, **params): """Fetches information of a certain resource.""" res = self.resource_path.get(resource, None) if not res: raise oc_exc.OpenContrailAPINotSupported(action='show', resource=resource) if res[-2:] == 's/': res = res[:-2] + '/' self._resource = resource self._id = id return self.get(res + id, params=params).popitem()[1] def create(self, resource, body): """Creates a new resource.""" res = self.resource_path.get(resource, None) if not res: raise oc_exc.OpenContrailAPINotSupported(action='create', resource=resource) self._resource = resource resp = self.post(res, body=body) if resp: return resp.popitem()[1] def update(self, resource, id, body=None): """Updates a resource.""" res = self.resource_path.get(resource, None) if not res: raise oc_exc.OpenContrailAPINotSupported(action='update', resource=resource) if res[-2:] == 's/': res = res[:-2] + '/' self._resource = resource self._id = id return self.put(res + id, body=body) def remove(self, resource, id): """Removes the specified resource.""" res = self.resource_path.get(resource, None) if not res: raise oc_exc.OpenContrailAPINotSupported(action='delete', resource=resource) if res[-2:] == 's/': res = res[:-2] + '/' self._resource = resource self._id = id return self.delete(res + id) def fqname_to_id(self, resource, fq_name): """Get UUID resource from an OpenContrail fq_name""" if not isinstance(fq_name, list): raise oc_exc.OpenContrailAPIBadFqName body = {'fq_name': fq_name, 'type': resource} return self.create('FQName to ID', body) def id_to_fqname(self, id): """Get fq_name resource from an OpenContrail UUID""" if not uuidutils.is_uuid_like(id): raise oc_exc.OpenContrailAPIBadUUID body = {'uuid': id} return self.create('ID to FQName', body) def ref_update(self, operation, resource_uuid, resource_type, ref_type, ref_fq_name, ref_uuid, attributes=None): """Updates a resource refference""" body = { "operation": operation, "type": resource_type, "uuid": resource_uuid, "ref-type": ref_type, "ref-fq-name": ref_fq_name, "ref-uuid": ref_uuid, } if attributes and isinstance(attributes, dict): body.update({'attr': attributes}) return self.create('Ref Update', body) def kv_store(self, operation, key=None, value=None): """Use key/value store exposed by the OpenContrail API""" body = { 'operation': operation, 'key': None, } if operation == 'RETRIEVE' and not key: pass elif operation in ['RETRIEVE', 'DELETE'] and key: body.update({'key': key}) elif operation == 'STORE' and key and value: body.update({'key': key, 'value': jsonutils.dumps(value)}) else: raise oc_exc.OpenContrailAPIBadKVAttributes return self.create('Key Value Store', body) networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/service_drivers/__init__.py0000666000175100017510000000000013245511235031604 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/services/__init__.py0000666000175100017510000000000013245511235026406 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/extensions/0000775000175100017510000000000013245511747024671 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/extensions/bgpvpn_vni.py0000666000175100017510000000206613245511235027411 0ustar zuulzuul00000000000000# # Copyright 2017 Ericsson India Global Services 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.api.definitions import bgpvpn_vni from neutron_lib.api import extensions class Bgpvpn_vni(extensions.APIExtensionDescriptor): """Extension class supporting vni. This class is used by neutron's extension framework to make metadata about the vni attribute in bgpvpn available to external applications. With admin rights one will be able to create and read the values. """ api_definition = bgpvpn_vni networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/extensions/__init__.py0000666000175100017510000000000013245511235026762 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/extensions/bgpvpn_routes_control.py0000666000175100017510000001103113245511271031666 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 import six from neutron.api import extensions from neutron.api.v2 import base from neutron_lib.api.definitions import bgpvpn as bgpvpn_api from neutron_lib.api.definitions import bgpvpn_routes_control as api_def from neutron_lib.api import extensions as api_extensions from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from neutron_lib.services import base as libbase from oslo_log import log from networking_bgpvpn._i18n import _ LOG = log.getLogger(__name__) class BGPVPNPortAssocNotFound(n_exc.NotFound): message = _("BGPVPN port association %(id)s could not be found " "for BGPVPN %(bgpvpn_id)s") class BGPVPNPortAssocAlreadyExists(n_exc.BadRequest): message = _("port %(port_id)s is already associated to " "BGPVPN %(bgpvpn_id)s") class BGPVPNPortAssocRouteNoSuchBGPVPN(n_exc.BadRequest): message = _("bgpvpn specified in route does not exist (%(bgpvpn_id)s)") class BGPVPNPortAssocRouteWrongBGPVPNTenant(n_exc.BadRequest): message = _("bgpvpn specified in route does not belong to the tenant " "(%(bgpvpn_id)s)") class Bgpvpn_routes_control(api_extensions.APIExtensionDescriptor): api_definition = api_def @classmethod def get_resources(cls): """Returns Ext Resources.""" # the plugin we link this extension with is the 'bgpvpn' plugin: plugin = directory.get_plugin(bgpvpn_api.LABEL) # The port association is the only new (sub-)resource # introduced by the bgpvpn-routes-control extension collection_name = api_def.PORT_ASSOCIATIONS resource_name = collection_name[:-1] parent = api_def.SUB_RESOURCE_ATTRIBUTE_MAP[ collection_name].get('parent') params = api_def.SUB_RESOURCE_ATTRIBUTE_MAP[ collection_name].get('parameters') controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=True, parent=parent, allow_pagination=True, allow_sorting=True) port_association_resource = extensions.ResourceExtension( collection_name, controller, parent, path_prefix='bgpvpn', attr_map=params) return [port_association_resource] # TODO(boden): remove with use of I8ae11633962a48de6e8559b85447b8c8c753d705 @classmethod def get_extended_resources(cls, version): if version == "2.0": return dict( list(api_def.RESOURCE_ATTRIBUTE_MAP.items()) + list(api_def.SUB_RESOURCE_ATTRIBUTE_MAP.items()) ) else: return {} @classmethod def get_plugin_interface(cls): return BGPVPNRoutesControlPluginBase @six.add_metaclass(abc.ABCMeta) class BGPVPNRoutesControlPluginBase(libbase.ServicePluginBase): @abc.abstractmethod def update_bgpvpn_router_association(self, context, assoc_id, bgpvpn_id, router_association): pass @abc.abstractmethod def create_bgpvpn_port_association(self, context, bgpvpn_id, port_association): pass @abc.abstractmethod def get_bgpvpn_port_association(self, context, assoc_id, bgpvpn_id, fields=None): pass @abc.abstractmethod def get_bgpvpn_port_associations(self, context, bgpvpn_id, filters=None, fields=None): pass @abc.abstractmethod def update_bgpvpn_port_association(self, context, assoc_id, bgpvpn_id, port_association): pass @abc.abstractmethod def delete_bgpvpn_port_association(self, context, assoc_id, bgpvpn_id): pass networking-bgpvpn-8.0.0/networking_bgpvpn/neutron/extensions/bgpvpn.py0000666000175100017510000001711013245511235026531 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 import six from neutron.api import extensions from neutron.api.v2 import base from neutron.api.v2 import resource_helper from neutron_lib.api.definitions import bgpvpn as bgpvpn_api_def from neutron_lib.api import extensions as api_extensions from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from neutron_lib.services import base as libbase from oslo_log import log from networking_bgpvpn._i18n import _ from networking_bgpvpn.neutron import extensions as bgpvpn_extensions LOG = log.getLogger(__name__) extensions.append_api_extensions_path(bgpvpn_extensions.__path__) class BGPVPNNotFound(n_exc.NotFound): message = _("BGPVPN %(id)s could not be found") class BGPVPNNetAssocNotFound(n_exc.NotFound): message = _("BGPVPN network association %(id)s could not be found " "for BGPVPN %(bgpvpn_id)s") class BGPVPNRouterAssocNotFound(n_exc.NotFound): message = _("BGPVPN router association %(id)s could not be found " "for BGPVPN %(bgpvpn_id)s") class BGPVPNTypeNotSupported(n_exc.BadRequest): message = _("BGPVPN %(driver)s driver does not support %(type)s type") class BGPVPNRDNotSupported(n_exc.BadRequest): message = _("BGPVPN %(driver)s driver does not support to manually set " "route distinguisher") class BGPVPNFindFromNetNotSupported(n_exc.BadRequest): message = _("BGPVPN %(driver)s driver does not support to fetch BGPVPNs " "associated to network id %(net_id)") class BGPVPNNetAssocAlreadyExists(n_exc.BadRequest): message = _("network %(net_id)s is already associated to " "BGPVPN %(bgpvpn_id)s") class BGPVPNRouterAssociationNotSupported(n_exc.BadRequest): message = _("BGPVPN %(driver)s driver does not support router " "associations") class BGPVPNRouterAssocAlreadyExists(n_exc.BadRequest): message = _("router %(router_id)s is already associated to " "BGPVPN %(bgpvpn_id)s") class BGPVPNMultipleRouterAssocNotSupported(n_exc.BadRequest): message = _("BGPVPN %(driver)s driver does not support multiple " "router association with a bgpvpn") class BGPVPNNetworkAssocExistsAnotherBgpvpn(n_exc.BadRequest): message = _("Network %(network)s already associated with %(bgpvpn)s. " "BGPVPN %(driver)s driver does not support same network" " associated to multiple bgpvpns") class BGPVPNDriverError(n_exc.NeutronException): message = _("%(method)s failed.") class Bgpvpn(api_extensions.APIExtensionDescriptor): api_definition = bgpvpn_api_def @classmethod def get_resources(cls): plural_mappings = resource_helper.build_plural_mappings( {}, bgpvpn_api_def.RESOURCE_ATTRIBUTE_MAP) resources = resource_helper.build_resource_info( plural_mappings, bgpvpn_api_def.RESOURCE_ATTRIBUTE_MAP, bgpvpn_api_def.LABEL, register_quota=True, translate_name=True) plugin = directory.get_plugin(bgpvpn_api_def.LABEL) for collection_name in bgpvpn_api_def.SUB_RESOURCE_ATTRIBUTE_MAP: # Special handling needed for sub-resources with 'y' ending # (e.g. proxies -> proxy) resource_name = collection_name[:-1] parent = bgpvpn_api_def.SUB_RESOURCE_ATTRIBUTE_MAP[ collection_name].get('parent') params = bgpvpn_api_def.SUB_RESOURCE_ATTRIBUTE_MAP[ collection_name].get('parameters') controller = base.create_resource(collection_name, resource_name, plugin, params, allow_bulk=True, parent=parent, allow_pagination=True, allow_sorting=True) resource = extensions.ResourceExtension( collection_name, controller, parent, path_prefix=bgpvpn_api_def.ALIAS, attr_map=params) resources.append(resource) return resources # TODO(boden): remove with use of I8ae11633962a48de6e8559b85447b8c8c753d705 @classmethod def get_extended_resources(cls, version): # we need to declare these extensions here so that they can be extended # by the bgpvpn-routes-control extension if version == "2.0": return dict(list(bgpvpn_api_def.RESOURCE_ATTRIBUTE_MAP.items()) + list(bgpvpn_api_def.SUB_RESOURCE_ATTRIBUTE_MAP.items()) ) else: return {} @classmethod def get_plugin_interface(cls): return BGPVPNPluginBase @six.add_metaclass(abc.ABCMeta) class BGPVPNPluginBase(libbase.ServicePluginBase): path_prefix = "/" + bgpvpn_api_def.ALIAS supported_extension_aliases = [bgpvpn_api_def.ALIAS] def get_plugin_name(self): return bgpvpn_api_def.LABEL def get_plugin_type(self): return bgpvpn_api_def.LABEL def get_plugin_description(self): return 'BGP VPN Interconnection service plugin' @abc.abstractmethod def create_bgpvpn(self, context, bgpvpn): pass @abc.abstractmethod def get_bgpvpns(self, context, filters=None, fields=None): pass @abc.abstractmethod def get_bgpvpn(self, context, id, fields=None): pass @abc.abstractmethod def update_bgpvpn(self, context, id, bgpvpn): pass @abc.abstractmethod def delete_bgpvpn(self, context, id): pass @abc.abstractmethod def create_bgpvpn_network_association(self, context, bgpvpn_id, network_association): pass @abc.abstractmethod def get_bgpvpn_network_association(self, context, assoc_id, bgpvpn_id, fields=None): pass @abc.abstractmethod def get_bgpvpn_network_associations(self, context, bgpvpn_id, filters=None, fields=None): pass @abc.abstractmethod def update_bgpvpn_network_association(self, context, assoc_id, bgpvpn_id, network_association): pass @abc.abstractmethod def delete_bgpvpn_network_association(self, context, assoc_id, bgpvpn_id): pass @abc.abstractmethod def create_bgpvpn_router_association(self, context, bgpvpn_id, router_association): pass @abc.abstractmethod def get_bgpvpn_router_association(self, context, assoc_id, bgpvpn_id, fields=None): pass @abc.abstractmethod def get_bgpvpn_router_associations(self, context, bgpvpn_id, filters=None, fields=None): pass @abc.abstractmethod def delete_bgpvpn_router_association(self, context, assoc_id, bgpvpn_id): pass networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/0000775000175100017510000000000013245511747023671 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/0000775000175100017510000000000013245511747025363 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/0000775000175100017510000000000013245511747026131 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/0000775000175100017510000000000013245511747027425 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/__init__.py0000666000175100017510000000000013245511235031516 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/bgpvpn.py0000777000175100017510000002213013245511235031266 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 __future__ import print_function from neutronclient.common import extension from neutronclient.neutron import v2_0 as neutronv20 from networking_bgpvpn._i18n import _ # To understand how neutronclient extensions work # read neutronclient/v2.0/client.py (extend_* methods and _register_extension) class BGPVPN(extension.NeutronClientExtension): resource = 'bgpvpn' resource_plural = '%ss' % resource object_path = '/bgpvpn/%s' % resource_plural resource_path = '/bgpvpn/%s/%%s' % resource_plural versions = ['2.0'] class BGPVPNCreateUpdateCommon(BGPVPN): def add_known_arguments(self, parser): """Adds to parser arguments common to create and update commands.""" parser.add_argument( '--name', help=_('Name of the BGP VPN')) parser.add_argument( '--route-targets', help=_('Route Targets list to import/export for this BGP ' 'VPN. Usage: -- --route-targets ' 'list=true : : ...')) parser.add_argument( '--import-targets', help=_('List of additional Route Targets to import from.' ' Usage: -- --import-targets list=true ' ': : ...')) parser.add_argument( '--export-targets', help=_('List of additional Route Targets to export to. Usage: -- ' '--export-targets list=true : : ...')) parser.add_argument( '--route-distinguishers', help=_('List of RDs that will be used to advertize VPN routes.' 'Usage: -- --route-distinguishers list=true ' ': : ...')) def args2body(self, parsed_args): body = { self.resource: {}, } neutronv20.update_dict(parsed_args, body[self.resource], ['name', 'tenant_id', 'type', 'route_targets', 'import_targets', 'export_targets', 'route_distinguishers']) return body class BGPVPNCreate(BGPVPNCreateUpdateCommon, extension.ClientExtensionCreate): """Create a BGPVPN.""" shell_command = 'bgpvpn-create' def add_known_arguments(self, parser): BGPVPNCreateUpdateCommon.add_known_arguments(self, parser) # type is read-only, hence specific to create parser.add_argument( '--type', default='l3', choices=['l2', 'l3'], help=_('BGP VPN type selection between L3VPN (l3) and ' 'EVPN (l2), default:l3')) class BGPVPNUpdate(BGPVPNCreateUpdateCommon, extension.ClientExtensionUpdate): """Update a given BGPVPN.""" shell_command = 'bgpvpn-update' class BGPVPNDelete(extension.ClientExtensionDelete, BGPVPN): """Delete a given BGPVPN.""" shell_command = 'bgpvpn-delete' class BGPVPNList(extension.ClientExtensionList, BGPVPN): """List BGPVPNs that belong to a given tenant.""" shell_command = 'bgpvpn-list' list_columns = [ 'id', 'name', 'type', 'route_targets', 'import_targets', 'export_targets', 'tenant_id', 'networks', 'routers'] pagination_support = True sorting_support = True class BGPVPNShow(extension.ClientExtensionShow, BGPVPN): """Show a given BGPVPN.""" shell_command = 'bgpvpn-show' # BGPVPN associations def _get_bgpvpn_id(client, name_or_id): return neutronv20.find_resourceid_by_name_or_id( client, BGPVPN.resource, name_or_id) class BGPVPNAssociation(object): def add_known_arguments(self, parser): parser.add_argument('bgpvpn', metavar='BGPVPN', help=_('ID or name of the BGPVPN.')) def set_extra_attrs(self, parsed_args): self.parent_id = _get_bgpvpn_id(self.get_client(), parsed_args.bgpvpn) # BGPVPN Network associations class BGPVPNNetAssoc(BGPVPNAssociation, extension.NeutronClientExtension): resource = 'network_association' resource_plural = '%ss' % resource # (parent_resource set to True so that the # first %s in *_path will be replaced with parent_id) parent_resource = True object_path = '%s/%s' % (BGPVPN.resource_path, resource_plural) resource_path = '%s/%s/%%%%s' % (BGPVPN.resource_path, resource_plural) versions = ['2.0'] allow_names = False # network associations have no name class BGPVPNNetAssocCreate(BGPVPNNetAssoc, extension.ClientExtensionCreate): """Create a BGPVPN-Network association.""" shell_command = "bgpvpn-net-assoc-create" def add_known_arguments(self, parser): BGPVPNNetAssoc.add_known_arguments(self, parser) parser.add_argument( '--network', required=True, help=_('ID or name of the network.')) def args2body(self, parsed_args): body = { self.resource: {}, } net = neutronv20.find_resourceid_by_name_or_id(self.get_client(), 'network', parsed_args.network) body[self.resource]['network_id'] = net neutronv20.update_dict(parsed_args, body[self.resource], ['tenant_id']) return body class BGPVPNNetAssocUpdate(extension.ClientExtensionUpdate, BGPVPNNetAssoc): """Update a given BGPVPN-Network association.""" shell_command = "bgpvpn-net-assoc-update" class BGPVPNNetAssocDelete(extension.ClientExtensionDelete, BGPVPNNetAssoc): """Delete a given BGPVPN-Network association.""" shell_command = "bgpvpn-net-assoc-delete" class BGPVPNNetAssocList(extension.ClientExtensionList, BGPVPNNetAssoc): """List BGPVPN-Network associations for a given BGPVPN.""" shell_command = "bgpvpn-net-assoc-list" list_columns = ['id', 'network_id'] pagination_support = True sorting_support = True class BGPVPNNetAssocShow(extension.ClientExtensionShow, BGPVPNNetAssoc): """Show a given BGPVPN-Network association.""" shell_command = "bgpvpn-net-assoc-show" # BGPVPN Router associations class BGPVPNRouterAssoc(BGPVPNAssociation, extension.NeutronClientExtension): resource = 'router_association' resource_plural = '%ss' % resource parent_resource = True object_path = '%s/%s' % (BGPVPN.resource_path, resource_plural) resource_path = '%s/%s/%%%%s' % (BGPVPN.resource_path, resource_plural) versions = ['2.0'] allow_names = False class BGPVPNRouterAssocCreate(BGPVPNRouterAssoc, extension.ClientExtensionCreate): """Create a BGPVPN-Router association.""" shell_command = "bgpvpn-router-assoc-create" def add_known_arguments(self, parser): BGPVPNRouterAssoc.add_known_arguments(self, parser) parser.add_argument( '--router', required=True, help=_('ID or name of the router.')) def args2body(self, parsed_args): body = { self.resource: {}, } router = neutronv20.find_resourceid_by_name_or_id(self.get_client(), 'router', parsed_args.router) body[self.resource]['router_id'] = router neutronv20.update_dict(parsed_args, body[self.resource], ['tenant_id']) return body class BGPVPNRouterAssocUpdate(extension.ClientExtensionUpdate, BGPVPNRouterAssoc): """Update a given BGPVPN-Router association.""" shell_command = "bgpvpn-router-assoc-update" class BGPVPNRouterAssocDelete(extension.ClientExtensionDelete, BGPVPNRouterAssoc): """Delete a given BGPVPN-Router association.""" shell_command = "bgpvpn-router-assoc-delete" class BGPVPNRouterAssocList(extension.ClientExtensionList, BGPVPNRouterAssoc): """List BGPVPN-Router associations for a given BGPVPN.""" shell_command = "bgpvpn-router-assoc-list" list_columns = ['id', 'router_id'] pagination_support = True sorting_support = True class BGPVPNRouterAssocShow(extension.ClientExtensionShow, BGPVPNRouterAssoc): """Show a given BGPVPN-Router association.""" shell_command = "bgpvpn-router-assoc-show" networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/__init__.py0000666000175100017510000000000013245511235030222 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/neutron/__init__.py0000666000175100017510000000000013245511235027454 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/neutronclient/__init__.py0000666000175100017510000000000013245511235025762 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/__init__.py0000666000175100017510000000121013245511235023075 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 pbr.version __version__ = pbr.version.VersionInfo( 'networking_bgpvpn').version_string() networking-bgpvpn-8.0.0/networking_bgpvpn/_i18n.py0000666000175100017510000000256613245511235022273 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 oslo_i18n DOMAIN = "networking_bgpvpn" _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" # requires oslo.i18n >=2.1.0 _C = _translators.contextual_form # The plural translation function using the name "_P" # requires oslo.i18n >=2.1.0 _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) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/0000775000175100017510000000000013245511747022142 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/0000775000175100017510000000000013245511747023121 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/db/0000775000175100017510000000000013245511747023506 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/db/test_db.py0000666000175100017510000005231513245511235025504 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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.api.definitions import bgpvpn_routes_control as bgpvpn_rc_def from neutron_lib.api.definitions import bgpvpn_vni as bgpvpn_vni_def from neutron_lib import context from networking_bgpvpn.neutron.db.bgpvpn_db import BGPVPNPluginDb from networking_bgpvpn.neutron.extensions.bgpvpn \ import BGPVPNNetAssocAlreadyExists from networking_bgpvpn.neutron.extensions.bgpvpn import BGPVPNNetAssocNotFound from networking_bgpvpn.neutron.extensions.bgpvpn import BGPVPNNotFound from networking_bgpvpn.neutron.services.common import constants from networking_bgpvpn.neutron.services.common import utils from networking_bgpvpn.tests.unit.services import test_plugin def _id_list(list): return [bgpvpn['id'] for bgpvpn in list] class BgpvpnDBTestCase(test_plugin.BgpvpnTestCaseMixin): def setUp(self, service_provider=None): super(BgpvpnDBTestCase, self).setUp(service_provider) self.ctx = context.get_admin_context() self.plugin_db = BGPVPNPluginDb() def test_bgpvpn_create_update_delete(self): with self.network() as net: # create bgpvpn = self.plugin_db.create_bgpvpn( self.ctx, {"tenant_id": self._tenant_id, "type": "l3", "name": "", "route_targets": ["64512:1"], "import_targets": ["64512:11", "64512:12"], "export_targets": ["64512:13", "64512:14"], "route_distinguishers": ["64512:15", "64512:16"], "vni": "1000", "local_pref": "777" } ) net_assoc = {'network_id': net['network']['id'], 'tenant_id': self._tenant_id} # associate network assoc1 = self.plugin_db.create_net_assoc(self.ctx, bgpvpn['id'], net_assoc) # retrieve bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, bgpvpn['id']) # check self.assertEqual("l3", bgpvpn['type']) # we could check tenant_id self.assertEqual(["64512:1"], bgpvpn['route_targets']) self.assertEqual(["64512:11", "64512:12"], bgpvpn['import_targets']) self.assertEqual(["64512:13", "64512:14"], bgpvpn['export_targets']) self.assertEqual(["64512:15", "64512:16"], bgpvpn['route_distinguishers']) if utils.is_extension_supported(self.bgpvpn_plugin, bgpvpn_vni_def.ALIAS): self.assertEqual(1000, bgpvpn['vni']) else: # # Test should ensure vni attribute is not present as # bpvpn_vni extension is not loaded. # self.assertFalse('vni' in bgpvpn) if utils.is_extension_supported(self.bgpvpn_plugin, bgpvpn_rc_def.ALIAS): self.assertEqual(777, bgpvpn['local_pref']) else: # # Test should ensure local_pref attribute is not present as # bpvpn-routes-control extension is not loaded. # self.assertFalse('local_pref' in bgpvpn) self.assertEqual([net['network']['id']], bgpvpn['networks']) assoc1 = self.plugin_db.get_net_assoc(self.ctx, assoc1['id'], bgpvpn['id']) self.assertEqual(net['network']['id'], assoc1['network_id']) self.assertEqual(bgpvpn['id'], assoc1['bgpvpn_id']) with self.network(name='net2') as net2: net_assoc2 = {'network_id': net2['network']['id'], 'tenant_id': self._tenant_id} # associate network assoc2 = self.plugin_db.create_net_assoc(self.ctx, bgpvpn['id'], net_assoc2) # retrieve assoc2 = self.plugin_db.get_net_assoc(self.ctx, assoc2['id'], bgpvpn['id']) assoc_list = self.plugin_db.get_net_assocs(self.ctx, bgpvpn['id']) self.assertIn(assoc2, assoc_list) self.assertIn(assoc1, assoc_list) self._test_router_assocs(bgpvpn['id'], 2) # update self.plugin_db.update_bgpvpn( self.ctx, bgpvpn['id'], {"type": "l2", "name": "foo", "tenant_id": "a-b-c-d", "route_targets": [], "import_targets": ["64512:22"], "route_distinguishers": [], "local_pref": "100" }) # retrieve bgpvpn2 = self.plugin_db.get_bgpvpn(self.ctx, bgpvpn['id']) # check self.assertEqual("l2", bgpvpn2['type']) self.assertEqual("a-b-c-d", bgpvpn2['tenant_id']) self.assertEqual("foo", bgpvpn2['name']) self.assertEqual([], bgpvpn2['route_targets']) self.assertEqual(["64512:22"], bgpvpn2['import_targets']) self.assertEqual(["64512:13", "64512:14"], bgpvpn2['export_targets']) self.assertEqual([], bgpvpn2['route_distinguishers']) self.assertEqual(100, bgpvpn2['local_pref']) # find bgpvpn by network_id bgpvpn3 = self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [net['network']['id']], }, ) self.assertEqual(1, len(bgpvpn3)) self.assertEqual(bgpvpn2['id'], bgpvpn3[0]['id']) # asset that GETting the assoc, but for another BGPVPN, does fails self.assertRaises(BGPVPNNetAssocNotFound, self.plugin_db.get_net_assoc, self.ctx, assoc2['id'], "bogus_bgpvpn_id") # assert that deleting a net remove the assoc self._delete('networks', net2['network']['id']) assoc_list = self.plugin_db.get_net_assocs(self.ctx, bgpvpn['id']) self.assertNotIn(assoc2, assoc_list) self.assertRaises(BGPVPNNetAssocNotFound, self.plugin_db.get_net_assoc, self.ctx, assoc2['id'], bgpvpn['id']) # delete self.plugin_db.delete_bgpvpn(self.ctx, bgpvpn['id']) # check that delete was effective self.assertRaises(BGPVPNNotFound, self.plugin_db.get_bgpvpn, self.ctx, bgpvpn['id']) # check that the assoc has been deleted after deleting the bgpvpn self.assertRaises(BGPVPNNetAssocNotFound, self.plugin_db.get_net_assoc, self.ctx, assoc1['id'], bgpvpn['id']) def _test_router_assocs(self, bgpvpn_id, max_assocs, assoc_count=0, previous_assocs=None): with self.router(tenant_id=self._tenant_id) as router: router_assoc = {'router_id': router['router']['id'], 'tenant_id': self._tenant_id} assoc = self.plugin_db.create_router_assoc(self.ctx, bgpvpn_id, router_assoc) assoc_count += 1 assoc = self.plugin_db.get_router_assoc(self.ctx, assoc['id'], bgpvpn_id) assoc_list = self.plugin_db.get_router_assocs(self.ctx, bgpvpn_id) bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, bgpvpn_id) self.assertIn(router['router']['id'], bgpvpn['routers']) if previous_assocs is None: previous_assocs = [] previous_assocs.append(assoc) for assoc in previous_assocs: self.assertIn(assoc, assoc_list) if assoc_count == max_assocs: return else: self._test_router_assocs(bgpvpn_id, max_assocs, assoc_count=assoc_count) def test_db_associate_disassociate_net(self): with self.network() as net: net_id = net['network']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] with self.assoc_net(id, net_id): bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, id) self.assertEqual([net_id], bgpvpn['networks']) bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, id) self.assertEqual([], bgpvpn['networks']) def test_db_associate_twice(self): with self.network() as net, self.bgpvpn() as bgpvpn: net_id = net['network']['id'] id = bgpvpn['bgpvpn']['id'] with self.assoc_net(id, net_id=net_id): self.assoc_net(id, net_id=net_id, do_disassociate=False) self.assertRaises(BGPVPNNetAssocAlreadyExists, self.plugin_db.create_net_assoc, self.ctx, id, {'tenant_id': self._tenant_id, 'network_id': net_id}) def test_db_find_bgpvpn_for_associated_network(self): with self.network() as net, \ self.bgpvpn(type=constants.BGPVPN_L2) as bgpvpn_l2, \ self.bgpvpn() as bgpvpn_l3, \ self.assoc_net(bgpvpn_l2['bgpvpn']['id'], net['network']['id']), \ self.assoc_net(bgpvpn_l3['bgpvpn']['id'], net['network']['id']): net_id = net['network']['id'] bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={'networks': [net_id]}, ) ) self.assertIn(bgpvpn_l2['bgpvpn']['id'], bgpvpn_id_list) self.assertIn(bgpvpn_l3['bgpvpn']['id'], bgpvpn_id_list) bgpvpn_l2_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [net_id], 'type': [constants.BGPVPN_L2], }, ) ) self.assertIn(bgpvpn_l2['bgpvpn']['id'], bgpvpn_l2_id_list) self.assertNotIn(bgpvpn_l3['bgpvpn']['id'], bgpvpn_l2_id_list) bgpvpn_l3_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [net_id], 'type': [constants.BGPVPN_L3], }, ) ) self.assertNotIn(bgpvpn_l2['bgpvpn']['id'], bgpvpn_l3_id_list[0]) self.assertIn(bgpvpn_l3['bgpvpn']['id'], bgpvpn_l3_id_list[0]) def test_db_delete_net(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] with self.network() as net: net_id = net['network']['id'] self.assoc_net(id, net_id=net_id, do_disassociate=False) bgpvpn_db = self.plugin_db.get_bgpvpn(self.ctx, id) self.assertEqual([], bgpvpn_db['networks']) def test_db_associate_disassociate_router(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] with self.assoc_router(id, router_id): bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, id) self.assertEqual([router_id], bgpvpn['routers']) bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, id) self.assertEqual([], bgpvpn['routers']) def test_db_find_bgpvpn_for_associated_router(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] with self.assoc_router(id, router_id=router_id): bgpvpn_list = self.plugin_db.get_bgpvpns( self.ctx, filters={'routers': [router_id]}, ) self.assertEqual(id, bgpvpn_list[0]['id']) def test_db_delete_router(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] self.assoc_router(id, router_id=router_id, do_disassociate=False) bgpvpn_db = self.plugin_db.get_bgpvpn(self.ctx, id) self.assertEqual([], bgpvpn_db['routers']) def test_db_list_bgpvpn_filtering_associated_resources(self): with self.network() as network1, \ self.network() as network2, \ self.router(tenant_id=self._tenant_id) as router1, \ self.router(tenant_id=self._tenant_id) as router2, \ self.bgpvpn() as bgpvpn1, \ self.bgpvpn() as bgpvpn2, \ self.bgpvpn() as bgpvpn3, \ self.assoc_net(bgpvpn1['bgpvpn']['id'], network1['network']['id']), \ self.assoc_router(bgpvpn3['bgpvpn']['id'], router1['router']['id']), \ self.assoc_net(bgpvpn2['bgpvpn']['id'], network2['network']['id']), \ self.assoc_router(bgpvpn2['bgpvpn']['id'], router2['router']['id']): network1_id = network1['network']['id'] network2_id = network2['network']['id'] router1_id = router1['router']['id'] router2_id = router2['router']['id'] bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [network1_id], }, ) ) self.assertIn(bgpvpn1['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn2['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn3['bgpvpn']['id'], bgpvpn_id_list) bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [network1_id, network2_id], }, ) ) self.assertIn(bgpvpn1['bgpvpn']['id'], bgpvpn_id_list) self.assertIn(bgpvpn2['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn3['bgpvpn']['id'], bgpvpn_id_list) bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'routers': [router1_id], }, ) ) self.assertNotIn(bgpvpn1['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn2['bgpvpn']['id'], bgpvpn_id_list) self.assertIn(bgpvpn3['bgpvpn']['id'], bgpvpn_id_list) bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'routers': [router1_id, router2_id], }, ) ) self.assertNotIn(bgpvpn1['bgpvpn']['id'], bgpvpn_id_list) self.assertIn(bgpvpn2['bgpvpn']['id'], bgpvpn_id_list) self.assertIn(bgpvpn3['bgpvpn']['id'], bgpvpn_id_list) bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [network1_id], 'routers': [router1_id], }, ) ) self.assertNotIn(bgpvpn1['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn2['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn3['bgpvpn']['id'], bgpvpn_id_list) bgpvpn_id_list = _id_list( self.plugin_db.get_bgpvpns( self.ctx, filters={ 'networks': [network2_id], 'routers': [router2_id], }, ) ) self.assertNotIn(bgpvpn1['bgpvpn']['id'], bgpvpn_id_list) self.assertIn(bgpvpn2['bgpvpn']['id'], bgpvpn_id_list) self.assertNotIn(bgpvpn3['bgpvpn']['id'], bgpvpn_id_list) def test_db_associate_disassociate_port(self): with self.port(tenant_id=self._tenant_id) as port, \ self.bgpvpn() as bgpvpn: port_id = port['port']['id'] bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.assoc_port(bgpvpn_id, port_id): bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, bgpvpn_id) self.assertEqual([port_id], bgpvpn['ports']) bgpvpn = self.plugin_db.get_bgpvpn(self.ctx, bgpvpn_id) self.assertEqual([], bgpvpn['ports']) def test_db_update_port_association(self): ROUTE_A = {'type': 'prefix', 'prefix': '12.1.0.0/16'} ROUTE_B = {'type': 'prefix', 'prefix': '14.0.0.0/8', 'local_pref': 200} ROUTE_Bbis = {'type': 'prefix', 'prefix': '14.0.0.0/8', 'local_pref': 100} ROUTE_C = {'type': 'prefix', 'prefix': '18.1.0.0/16'} def with_defaults(port_assoc_route): r = dict(local_pref=None) r.update(port_assoc_route) return r with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id'], advertise_fixed_ips=False, routes=[ROUTE_A, ROUTE_B]) as port_assoc: bgpvpn_id = bgpvpn['bgpvpn']['id'] self._update('bgpvpn/bgpvpns/%s/port_associations' % bgpvpn_id, port_assoc['port_association']['id'], {'port_association': {'advertise_fixed_ips': True, 'routes': [ROUTE_Bbis, ROUTE_C]} }) assoc = self.show_port_assoc( bgpvpn['bgpvpn']['id'], port_assoc['port_association']['id']) assoc = assoc['port_association'] self.assertTrue(assoc['advertise_fixed_ips']) self.assertNotIn(with_defaults(ROUTE_A), assoc['routes']) self.assertIn(with_defaults(ROUTE_Bbis), assoc['routes']) self.assertIn(with_defaults(ROUTE_C), assoc['routes']) res = self._update( 'bgpvpn/bgpvpns/%s/port_associations' % bgpvpn_id, port_assoc['port_association']['id'], {'port_association': {'routes': []}} ) self.assertEqual(0, len(res['port_association']['routes'])) class BgpvpnDBTestCaseWithVNI(BgpvpnDBTestCase): def setUp(self): test_service_provider = ('networking_bgpvpn.tests.unit.services' '.test_plugin.TestBgpvpnDriverWithVni') super(BgpvpnDBTestCaseWithVNI, self).setUp( service_provider=test_service_provider) class BgpvpnDBTestCaseWithRC(BgpvpnDBTestCase): def setUp(self): test_service_provider = ('networking_bgpvpn.neutron.services.' 'service_drivers.driver_api.BGPVPNDriverRC') super(BgpvpnDBTestCaseWithRC, self).setUp( service_provider=test_service_provider) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/db/__init__.py0000666000175100017510000000000013245511235025577 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/__init__.py0000666000175100017510000000000013245511235025212 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/0000775000175100017510000000000013245511747024744 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/bagpipe/0000775000175100017510000000000013245511747026353 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/bagpipe/test_bagpipe.py0000666000175100017510000013703613245511235031377 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 copy import mock import webob.exc from oslo_config import cfg from neutron.api.rpc.handlers import resources_rpc from neutron.db import agents_db from neutron.db import db_base_plugin_v2 from neutron.debug import debug_agent from neutron.plugins.ml2 import plugin as ml2_plugin from neutron.plugins.ml2 import rpc as ml2_rpc from neutron.tests.common import helpers from neutron_lib.api.definitions import portbindings from neutron_lib import constants as const from neutron_lib import context as n_context from neutron_lib.plugins import directory from networking_bgpvpn.neutron.services.service_drivers.bagpipe import bagpipe from networking_bgpvpn.tests.unit.services import test_plugin from networking_bagpipe.objects import bgpvpn as objs def _expected_formatted_bgpvpn(id, net_id, rt=None, gateway_mac=None): return {'id': id, 'network_id': net_id, 'l3vpn': {'import_rt': rt or mock.ANY, 'export_rt': rt or mock.ANY}, 'gateway_mac': gateway_mac or mock.ANY} class TestCorePluginWithAgents(db_base_plugin_v2.NeutronDbPluginV2, agents_db.AgentDbMixin): pass class TestBagpipeCommon(test_plugin.BgpvpnTestCaseMixin): def setUp(self, plugin=None, driver=('networking_bgpvpn.neutron.services.service_drivers.' 'bagpipe.bagpipe.BaGPipeBGPVPNDriver')): self.mocked_rpc = mock.patch( 'networking_bagpipe.agent.bgpvpn.rpc_client' '.BGPVPNAgentNotifyApi').start().return_value self.mock_attach_rpc = self.mocked_rpc.attach_port_on_bgpvpn self.mock_detach_rpc = self.mocked_rpc.detach_port_from_bgpvpn self.mock_update_rpc = self.mocked_rpc.update_bgpvpn self.mock_delete_rpc = self.mocked_rpc.delete_bgpvpn mock.patch( 'neutron.common.rpc.get_client').start().return_value if not plugin: plugin = '%s.%s' % (__name__, TestCorePluginWithAgents.__name__) super(TestBagpipeCommon, self).setUp(service_provider=driver, core_plugin=plugin) self.ctxt = n_context.Context('fake_user', self._tenant_id) n_dict = {"name": "netfoo", "tenant_id": self._tenant_id, "admin_state_up": True, "router:external": True, "shared": True} self.external_net = {'network': self.plugin.create_network(self.ctxt, {'network': n_dict})} class AnyOfClass(object): def __init__(self, cls): self._class = cls def __eq__(self, other): return isinstance(other, self._class) def __repr__(self): return "AnyOfClass<%s>" % self._class.__name__ class TestBagpipeOVOPushPullMixin(object): # tests for OVO-based push notifications go here @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_bgpvpn_update_name_only(self, mocked_push): with self.bgpvpn() as bgpvpn: self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], {'bgpvpn': {'name': 'newname'}}) # check that no RPC push is done for BGPVPN objects self.assertTrue( mocked_push.call_count == 0 or (not any([isinstance(ovo, objs.BGPVPNNetAssociation) for ovo in mocked_push.mock_calls[0][1][1]]) and not any([isinstance(ovo, objs.BGPVPNRouterAssociation) for ovo in mocked_push.mock_calls[0][1][1]]) ) ) @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_bgpvpn_update_rts_no_assoc(self, mocked_push): with self.bgpvpn() as bgpvpn: self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], {'bgpvpn': {'route_targets': ['64512:43']}}) # check that no RPC push is done for BGPVPN objects self.assertTrue( mocked_push.call_count == 0 or (not any([isinstance(ovo, objs.BGPVPNNetAssociation) for ovo in mocked_push.mock_calls[0][1][1]]) and not any([isinstance(ovo, objs.BGPVPNRouterAssociation) for ovo in mocked_push.mock_calls[0][1][1]]) ) ) @mock.patch.object(resources_rpc.ResourcesPushRpcApi, '_push') def test_bgpvpn_update_delete_rts_with_assocs(self, mocked_push): with self.bgpvpn(do_delete=False) as bgpvpn, \ self.network() as net, \ self.router(tenant_id=self._tenant_id) as router, \ self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id'], do_disassociate=False), \ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id'], do_disassociate=False): mocked_push.reset_mock() self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], {'bgpvpn': {'route_targets': ['64512:43']}}) mocked_push.assert_any_call(mock.ANY, 'BGPVPNNetAssociation', mock.ANY, 'updated') mocked_push.assert_any_call(mock.ANY, 'BGPVPNRouterAssociation', mock.ANY, 'updated') mocked_push.reset_mock() # delete BGPVPN self._delete('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) # after delete mocked_push.assert_any_call(mock.ANY, 'BGPVPNNetAssociation', mock.ANY, 'deleted') mocked_push.assert_any_call(mock.ANY, 'BGPVPNRouterAssociation', mock.ANY, 'deleted') @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_net_assoc_create_delete(self, mocked_push): with self.network() as net, \ self.bgpvpn() as bgpvpn: mocked_push.reset_mock() with self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id']): mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'created') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNNetAssociation)], ovos_in_call ) mocked_push.reset_mock() # after net assoc delete mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'deleted') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNNetAssociation)], ovos_in_call ) @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_router_assoc_create_delete(self, mocked_push): with self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn: mocked_push.reset_mock() with self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']): mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'created') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNRouterAssociation)], ovos_in_call ) mocked_push.reset_mock() # after router assoc delete mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'deleted') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNRouterAssociation)], ovos_in_call ) @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_port_assoc_crud(self, mocked_push): with self.port() as port, \ self.bgpvpn() as bgpvpn: mocked_push.reset_mock() with self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as port_assoc: mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'created') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNPortAssociation)], ovos_in_call ) mocked_push.reset_mock() self._update( ('bgpvpn/bgpvpns/%s/port_associations' % bgpvpn['bgpvpn']['id']), port_assoc['port_association']['id'], {'port_association': {'advertise_fixed_ips': False}}) mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'updated') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNPortAssociation)], ovos_in_call ) mocked_push.reset_mock() # after port assoc delete mocked_push.assert_called_once_with(mock.ANY, mock.ANY, 'deleted') ovos_in_call = mocked_push.mock_calls[0][1][1] self.assertEqual( [AnyOfClass(objs.BGPVPNPortAssociation)], ovos_in_call ) class TestBagpipeServiceDriver(TestBagpipeCommon): def test_create_bgpvpn_l2_fails(self): bgpvpn_data = copy.copy(self.bgpvpn_data['bgpvpn']) bgpvpn_data.update({"type": "l2"}) # Assert that an error is returned to the client bgpvpn_req = self.new_create_request( 'bgpvpn/bgpvpns', bgpvpn_data) res = bgpvpn_req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_create_bgpvpn_rds_fails(self): bgpvpn_data = copy.copy(self.bgpvpn_data) bgpvpn_data['bgpvpn'].update({"route_distinguishers": ["4444:55"]}) # Assert that an error is returned to the client bgpvpn_req = self.new_create_request( 'bgpvpn/bgpvpns', bgpvpn_data) res = bgpvpn_req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_bagpipe_update_bgpvpn_rds_fails(self): with self.bgpvpn() as bgpvpn: update_data = {'bgpvpn': {"route_distinguishers": ["4444:55"]}} self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], update_data, expected_code=webob.exc.HTTPBadRequest.code) show_bgpvpn = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertEqual([], show_bgpvpn['bgpvpn']['route_distinguishers']) def test_bagpipe_associate_net(self): with self.port() as port1: net_id = port1['port']['network_id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = bgpvpn['bgpvpn']['route_targets'] self.mock_update_rpc.reset_mock() with self.assoc_net(id, net_id): self.mock_update_rpc.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn(id, net_id, rt)) def test_bagpipe_associate_external_net_failed(self): net_id = self.external_net['network']['id'] with self.bgpvpn(tenant_id='another_tenant') as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'network_association': {'network_id': net_id, 'tenant_id': self._tenant_id}} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) def test_bagpipe_associate_router(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.subnet() as subnet: with self.port(subnet=subnet) as port: net_id = port['port']['network_id'] subnet_id = subnet['subnet']['id'] itf = self._router_interface_action('add', router_id, subnet_id, None) itf_port = self.plugin.get_port(self.ctxt, itf['port_id']) with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = bgpvpn['bgpvpn']['route_targets'] self.mock_update_rpc.reset_mock() with self.assoc_router(id, router_id): self.mock_update_rpc.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn( id, net_id, rt, itf_port['mac_address'])) def test_bagpipe_disassociate_net(self): mocked_delete = self.mocked_rpc.delete_bgpvpn with self.port() as port1: net_id = port1['port']['network_id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = bgpvpn['bgpvpn']['route_targets'] with self.assoc_net(id, net_id, do_disassociate=False) as assoc: mocked_delete.reset_mock() del_req = self.new_delete_request( 'bgpvpn/bgpvpns', id, fmt=self.fmt, subresource='network_associations', sub_id=assoc['network_association']['id']) res = del_req.get_response(self.ext_api) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) mocked_delete.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn(id, net_id, rt)) def test_bagpipe_update_bgpvpn_rt(self): with self.port() as port1: net_id = port1['port']['network_id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = ['6543:21'] with self.assoc_net(id, net_id): update_data = {'bgpvpn': {'route_targets': ['6543:21']}} self.mock_update_rpc.reset_mock() self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], update_data) self.mock_update_rpc.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn(id, net_id, rt)) def test_bagpipe_update_bgpvpn_with_router_assoc(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']), \ self.port(subnet=subnet): self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) update_data = {'bgpvpn': {'route_targets': ['6543:21']}} self.mock_update_rpc.reset_mock() self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], update_data) self.mock_update_rpc.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn(bgpvpn['bgpvpn']['id'], net['network']['id'])) def test_bagpipe_delete_bgpvpn(self): mocked_delete = self.mocked_rpc.delete_bgpvpn with self.port() as port1: net_id = port1['port']['network_id'] with self.bgpvpn(do_delete=False) as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = bgpvpn['bgpvpn']['route_targets'] mocked_delete.reset_mock() with self.assoc_net(id, net_id, do_disassociate=False): self._delete('bgpvpn/bgpvpns', id) mocked_delete.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn(id, net_id, rt)) def test_bagpipe_delete_bgpvpn_with_router_assoc(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn(do_delete=False) as bgpvpn, \ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id'], do_disassociate=False), \ self.port(subnet=subnet): self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) self.mock_delete_rpc.reset_mock() self._delete('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.mocked_rpc.delete_bgpvpn.assert_called_once_with( mock.ANY, _expected_formatted_bgpvpn(bgpvpn['bgpvpn']['id'], net['network']['id'])) def test_bagpipe_callback_to_rpc_update_port_after_router_itf_added(self): driver = self.bgpvpn_plugin.driver with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn: itf = self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) with self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']), \ self.port(subnet=subnet) as port: mac_address = port['port']['mac_address'] formatted_ip = (port['port']['fixed_ips'][0]['ip_address'] + '/' + subnet['subnet']['cidr'].split('/')[-1]) itf_port = self.plugin.get_port(self.ctxt, itf['port_id']) expected = { 'gateway_ip': subnet['subnet']['gateway_ip'], 'mac_address': mac_address, 'ip_address': formatted_ip, 'gateway_mac': itf_port['mac_address'] } expected.update(driver._format_bgpvpn_network_route_targets( [bgpvpn['bgpvpn']])) actual = driver._retrieve_bgpvpn_network_info_for_port( self.ctxt, port['port']) self.assertEqual(expected, actual) def test_bagpipe_get_network_info_for_port(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.port(subnet=subnet) as port: itf = self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) itf_port = self.plugin.get_port(self.ctxt, itf['port_id']) r = bagpipe.get_network_info_for_port(self.ctxt, port['port']['id'], net['network']['id']) expected_ip = port['port']['fixed_ips'][0]['ip_address'] + "/24" self.assertEqual({ 'mac_address': port['port']['mac_address'], 'ip_address': expected_ip, 'gateway_ip': subnet['subnet']['gateway_ip'], 'gateway_mac': itf_port['mac_address'] }, r) RT = '12345:1' BGPVPN_INFO = {'mac_address': 'de:ad:00:00:be:ef', 'ip_address': '10.0.0.2', 'gateway_ip': '10.0.0.1', 'l3vpn': {'import_rt': [RT], 'export_rt': [RT] }, 'gateway_mac': None } class TestCorePluginML2WithAgents(ml2_plugin.Ml2Plugin, agents_db.AgentDbMixin): pass class TestBagpipeServiceDriverCallbacks(TestBagpipeCommon, TestBagpipeOVOPushPullMixin): '''Check that receiving callbacks results in RPC calls to the agent''' def setUp(self): cfg.CONF.set_override('mechanism_drivers', ['logger', 'fake_agent'], 'ml2') super(TestBagpipeServiceDriverCallbacks, self).setUp( "%s.%s" % (__name__, TestCorePluginML2WithAgents.__name__)) self.port_create_status = 'DOWN' self.plugin = directory.get_plugin() self.plugin.start_rpc_listeners() self.bagpipe_driver = self.bgpvpn_plugin.driver self.patched_driver = mock.patch.object( self.bgpvpn_plugin.driver, '_retrieve_bgpvpn_network_info_for_port', return_value=BGPVPN_INFO) self.patched_driver.start() # we choose an agent of type const.AGENT_TYPE_OFA # because this is the type used by the fake_agent mech driver helpers.register_ovs_agent(helpers.HOST, const.AGENT_TYPE_OFA) helpers.register_l3_agent() def _build_expected_return_active(self, port): bgpvpn_info_port = BGPVPN_INFO.copy() bgpvpn_info_port.update({'id': port['id'], 'network_id': port['network_id']}) return bgpvpn_info_port def _build_expected_return_down(self, port): return {'id': port['id'], 'network_id': port['network_id']} def _update_port_status(self, port, status): network_id = port['port']['network_id'] some_network = {'id': network_id} self.plugin.get_network = mock.Mock(return_value=some_network) self.plugin.update_port_status(self.ctxt, port['port']['id'], status, helpers.HOST) def test_bagpipe_callback_to_rpc_update_down2active(self): with self.port(arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.mock_attach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_active(port['port']), helpers.HOST) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_update_active2down(self): with self.port(arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_detach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_down(port['port']), helpers.HOST) self.assertFalse(self.mock_attach_rpc.called) def test_bagpipe_callback_to_rpc_update_active2active(self): with self.port(arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_update_down2down(self): with self.port(arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_DOWN) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_deleted(self): with self.port(arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self.plugin.delete_port(self.ctxt, port['port']['id']) self.mock_detach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_down(port['port']), helpers.HOST) self.assertFalse(self.mock_attach_rpc.called) def test_bagpipe_callback_to_rpc_update_active_ignore_net_ports(self): with self.port(device_owner=const.DEVICE_OWNER_NETWORK_PREFIX, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_dont_ignore_probe_ports_compute(self): with self.port(device_owner=debug_agent.DEVICE_OWNER_COMPUTE_PROBE, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.mock_attach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_active(port['port']), helpers.HOST) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_dont_ignore_probe_ports_network(self): with self.port(device_owner=debug_agent.DEVICE_OWNER_NETWORK_PROBE, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.mock_attach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_active(port['port']), helpers.HOST) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_update_down_ignore_net_ports(self): with self.port(device_owner=const.DEVICE_OWNER_NETWORK_PREFIX, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_deleted_ignore_net_ports(self): with self.port(device_owner=const.DEVICE_OWNER_NETWORK_PREFIX, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self.bagpipe_driver.registry_port_deleted( None, None, None, context=self.ctxt, port_id=port['port']['id'] ) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_update_active_ignore_external_net(self): with self.subnet(network=self.external_net) as subnet, \ self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_update_down_ignore_external_net(self): with self.subnet(network=self.external_net) as subnet, \ self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_ACTIVE) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self._update_port_status(port, const.PORT_STATUS_DOWN) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_bagpipe_callback_to_rpc_deleted_ignore_external_net(self): with self.subnet(network=self.external_net) as subnet, \ self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port: self._update_port_status(port, const.PORT_STATUS_DOWN) self.mock_attach_rpc.reset_mock() self.mock_detach_rpc.reset_mock() self.bagpipe_driver.registry_port_deleted( None, None, None, context=self.ctxt, port_id=port['port']['id'] ) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) def test_delete_port_to_bgpvpn_rpc(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port, \ mock.patch.object(self.plugin, 'get_port', return_value=port['port']), \ mock.patch.object(self.plugin, 'get_network', return_value=net['network']): self.plugin.delete_port(self.ctxt, port['port']['id']) self.mock_detach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_down(port['port']), helpers.HOST) def test_bagpipe_callback_to_rpc_update_port_router_itf_added(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.port(subnet=subnet) as port, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ mock.patch.object(self.bagpipe_driver, 'get_bgpvpn', return_value=bgpvpn['bgpvpn']),\ mock.patch.object(bagpipe, 'get_router_bgpvpn_assocs', return_value=[{ 'bgpvpn_id': bgpvpn['bgpvpn']['id'] }]).start(): self.bagpipe_driver.registry_router_interface_created( None, None, None, context=self.ctxt, port={'network_id': net['network']['id']}, router_id=router['router']['id'], ) self.mock_update_rpc.assert_called_once_with( mock.ANY, self.bagpipe_driver._format_bgpvpn(self.ctxt, bgpvpn['bgpvpn'], port['port']['network_id'])) def test_bagpipe_callback_to_rpc_update_port_router_itf_removed(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.port(subnet=subnet) as port, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ mock.patch.object(self.bagpipe_driver, 'get_bgpvpn', return_value=bgpvpn['bgpvpn']),\ mock.patch.object(bagpipe, 'get_router_bgpvpn_assocs', return_value=[{ 'bgpvpn_id': bgpvpn['bgpvpn']['id'] }]).start(): self.bagpipe_driver.registry_router_interface_deleted( None, None, None, context=self.ctxt, network_id=port['port']['network_id'], port={'device_id': router['router']['id'], 'network_id': net['network']['id']} ) self.mock_delete_rpc.assert_called_once_with( mock.ANY, self.bagpipe_driver._format_bgpvpn(self.ctxt, bgpvpn['bgpvpn'], port['port']['network_id'])) def test_l3agent_add_remove_router_interface_to_bgpvpn_rpc(self): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}), \ mock.patch.object(bagpipe, 'get_router_bgpvpn_assocs', return_value=[{ 'bgpvpn_id': bgpvpn['bgpvpn']['id'] }]).start(): self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) self.mock_update_rpc.assert_called_once_with( mock.ANY, self.bagpipe_driver._format_bgpvpn(self.ctxt, bgpvpn['bgpvpn'], net['network']['id'])) self._router_interface_action('remove', router['router']['id'], subnet['subnet']['id'], None) self.mock_delete_rpc.assert_called_once_with( mock.ANY, self.bagpipe_driver._format_bgpvpn(self.ctxt, bgpvpn['bgpvpn'], net['network']['id'])) def test_gateway_mac_info_rpc(self): BGPVPN_INFO_GW_MAC = copy.copy(BGPVPN_INFO) BGPVPN_INFO_GW_MAC.update(gateway_mac='aa:bb:cc:dd:ee:ff') self.patched_driver.stop() with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn(route_targets=[RT]) as bgpvpn, \ self.port(subnet=subnet, arg_list=(portbindings.HOST_ID,), **{portbindings.HOST_ID: helpers.HOST}) as port, \ self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id']), \ mock.patch.object(self.bgpvpn_plugin.driver, 'retrieve_bgpvpns_of_router_assocs' '_by_network', return_value=[{'type': 'l3', 'route_targets': [RT]}] ): self._update_port_status(port, const.PORT_STATUS_ACTIVE) itf = self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) itf_port = self.plugin.get_port(self.ctxt, itf['port_id']) self.mock_update_rpc.assert_called_with( mock.ANY, _expected_formatted_bgpvpn(bgpvpn['bgpvpn']['id'], net['network']['id'], [RT], gateway_mac=itf_port['mac_address']) ) self._router_interface_action('remove', router['router']['id'], subnet['subnet']['id'], None) self.mock_update_rpc.assert_called_with( mock.ANY, _expected_formatted_bgpvpn(bgpvpn['bgpvpn']['id'], net['network']['id'], [RT], gateway_mac=None) ) self.patched_driver.start() def test_l2agent_rpc_to_bgpvpn_rpc(self): # # Test that really simulate the ML2 codepath that # generate the registry events. ml2_rpc_callbacks = ml2_rpc.RpcCallbacks(mock.Mock(), mock.Mock()) n_dict = {"name": "netfoo", "tenant_id": self._tenant_id, "admin_state_up": True, "shared": False} net = self.plugin.create_network(self.ctxt, {'network': n_dict}) subnet_dict = {'name': 'test_subnet', 'tenant_id': self._tenant_id, 'ip_version': 4, 'cidr': '10.0.0.0/24', 'allocation_pools': [{'start': '10.0.0.2', 'end': '10.0.0.254'}], 'enable_dhcp': False, 'dns_nameservers': [], 'host_routes': [], 'network_id': net['id']} self.plugin.create_subnet(self.ctxt, {'subnet': subnet_dict}) p_dict = {'network_id': net['id'], 'tenant_id': self._tenant_id, 'name': 'fooport', "admin_state_up": True, "device_id": "tapfoo", "device_owner": "not_me", "mac_address": "de:ad:00:00:be:ef", "fixed_ips": [], "binding:host_id": helpers.HOST, } port = self.plugin.create_port(self.ctxt, {'port': p_dict}) ml2_rpc_callbacks.update_device_up(self.ctxt, host=helpers.HOST, agent_id='fooagent', device="de:ad:00:00:be:ef") self.mock_attach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_active(port), helpers.HOST) ml2_rpc_callbacks.update_device_down(self.ctxt, host=helpers.HOST, agent_id='fooagent', device="de:ad:00:00:be:ef") self.mock_detach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_down(port), helpers.HOST) self.mock_detach_rpc.reset_mock() self.plugin.delete_port(self.ctxt, port['id']) self.mock_detach_rpc.assert_called_once_with( mock.ANY, self._build_expected_return_down(port), helpers.HOST) def test_exception_on_callback(self): with mock.patch.object(bagpipe.LOG, 'exception') as log_exc: self.bagpipe_driver.registry_port_updated( None, None, None, context=self.ctxt, port=None ) self.assertFalse(self.mock_attach_rpc.called) self.assertFalse(self.mock_detach_rpc.called) self.assertTrue(log_exc.called) def test_format_bgpvpn_network_route_targets(self): driver = self.bgpvpn_plugin.driver bgpvpns = [{ 'type': 'l3', 'route_targets': ['12345:1', '12345:2', '12345:3'], 'import_targets': ['12345:2', '12345:3'], 'export_targets': ['12345:3', '12345:4'] }, { 'type': 'l3', 'route_targets': ['12345:3', '12346:1'] }, { 'type': 'l2', 'route_targets': ['12347:1'] }] result = driver._format_bgpvpn_network_route_targets(bgpvpns) expected = { 'l3vpn': { 'import_rt': ['12345:1', '12345:2', '12345:3', '12346:1'], 'export_rt': ['12345:1', '12345:2', '12345:3', '12345:4', '12346:1'] }, 'l2vpn': { 'import_rt': ['12347:1'], 'export_rt': ['12347:1'] } } self.assertItemsEqual(result['l3vpn']['import_rt'], expected['l3vpn']['import_rt']) self.assertItemsEqual(result['l3vpn']['export_rt'], expected['l3vpn']['export_rt']) self.assertItemsEqual(result['l2vpn']['import_rt'], expected['l2vpn']['import_rt']) self.assertItemsEqual(result['l2vpn']['export_rt'], expected['l2vpn']['export_rt']) class TestBagpipeServiceDriverV2RPCs(TestBagpipeCommon, TestBagpipeOVOPushPullMixin): '''Check RPC push/pull and local registry callback effects''' def setUp(self): cfg.CONF.set_override('mechanism_drivers', ['logger', 'fake_agent'], 'ml2') super(TestBagpipeServiceDriverV2RPCs, self).setUp( "%s.%s" % (__name__, TestCorePluginML2WithAgents.__name__), driver=('networking_bgpvpn.neutron.services.service_drivers.' 'bagpipe.bagpipe_v2.BaGPipeBGPVPNDriver')) @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_router_itf_event_router_assoc(self, mocked_push): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']): mocked_push.reset_mock() itf = self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) mocked_push.assert_any_call( mock.ANY, [AnyOfClass(objs.BGPVPNRouterAssociation)], 'updated') mocked_push.reset_mock() itf = self._router_interface_action('remove', router['router']['id'], subnet['subnet']['id'], itf['port_id']) mocked_push.assert_any_call( mock.ANY, [AnyOfClass(objs.BGPVPNRouterAssociation)], 'updated') @mock.patch.object(resources_rpc.ResourcesPushRpcApi, 'push') def test_router_itf_event_network_assoc(self, mocked_push): with self.network() as net, \ self.subnet(network=net) as subnet, \ self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id']): mocked_push.reset_mock() itf = self._router_interface_action('add', router['router']['id'], subnet['subnet']['id'], None) mocked_push.assert_any_call( mock.ANY, [AnyOfClass(objs.BGPVPNNetAssociation)], 'updated') mocked_push.reset_mock() itf = self._router_interface_action('remove', router['router']['id'], subnet['subnet']['id'], itf['port_id']) mocked_push.assert_any_call( mock.ANY, [AnyOfClass(objs.BGPVPNNetAssociation)], 'updated') ovos_in_call = mocked_push.mock_calls[0][1][1] for ovo in ovos_in_call: if not isinstance(ovo, objs.BGPVPNNetAssociation): continue for subnet in ovo.all_subnets(net['network']['id']): self.assertIsNone(subnet['gateway_mac']) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/bagpipe/__init__.py0000666000175100017510000000000013245511235030444 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/common/0000775000175100017510000000000013245511747026234 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/common/test_utils.py0000666000175100017510000000446313245511235031006 0ustar zuulzuul00000000000000# Copyright (c) 2017 Juniper Networks, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.tests import base from networking_bgpvpn.neutron.services.common.utils import filter_resource class TestFilterResource(base.BaseTestCase): _fake_resource_string = { 'fake_attribute': 'fake_value1', } _fake_resource_list = { 'fake_attribute': ['fake_value1', 'fake_value2', 'fake_value3'], } def test_filter_resource_succeeds_with_one_value(self): filters = { 'fake_attribute': 'fake_value1', } self.assertTrue(filter_resource(self._fake_resource_string, filters)) self.assertTrue(filter_resource(self._fake_resource_list, filters)) def test_filter_resource_fails_with_one_value(self): filters = { 'fake_attribute': 'wrong_fake_value1', } self.assertFalse(filter_resource(self._fake_resource_string, filters)) self.assertFalse(filter_resource(self._fake_resource_list, filters)) def test_filter_resource_succeeds_with_list_of_values(self): filters = { 'fake_attribute': ['fake_value1'], } self.assertTrue(filter_resource(self._fake_resource_string, filters)) filters = { 'fake_attribute': ['fake_value1', 'fake_value2'], } self.assertTrue(filter_resource(self._fake_resource_list, filters)) def test_filter_resource_fails_with_list_of_values(self): filters = { 'fake_attribute': ['wrong_fake_value1'], } self.assertFalse(filter_resource(self._fake_resource_string, filters)) filters = { 'fake_attribute': ['wrong_fake_value1', 'fake_value2'], } self.assertFalse(filter_resource(self._fake_resource_list, filters)) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/common/__init__.py0000666000175100017510000000000013245511235030325 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/odl/0000775000175100017510000000000013245511747025522 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/odl/__init__.py0000666000175100017510000000000013245511235027613 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/odl/test_odl.py0000666000175100017510000000727413245511235027715 0ustar zuulzuul00000000000000# # Copyright (C) 2015 Ericsson India Global Services 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 networking_bgpvpn.tests.unit.services import test_plugin class TestBgpvpnOdlCommon(test_plugin.BgpvpnTestCaseMixin): def setUp(self): self.mocked_odlclient = mock.patch( 'networking_odl.common.client' '.OpenDaylightRestClient.create_client').start().return_value provider = ('networking_bgpvpn.neutron.services.service_drivers.' 'opendaylight.odl.OpenDaylightBgpvpnDriver') super(TestBgpvpnOdlCommon, self).setUp(service_provider=provider) class TestOdlServiceDriver(TestBgpvpnOdlCommon): def test_odl_associate_net(self): mocked_sendjson = self.mocked_odlclient.sendjson with self.port() as port1: net_id = port1['port']['network_id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = bgpvpn['bgpvpn']['route_targets'] mocked_sendjson.reset_mock() with self.assoc_net(id, net_id): formatted_bgpvpn = { 'bgpvpn': {'export_targets': mock.ANY, 'name': mock.ANY, 'route_targets': rt, 'tenant_id': mock.ANY, 'project_id': mock.ANY, 'import_targets': mock.ANY, 'route_distinguishers': mock.ANY, 'type': mock.ANY, 'id': id, 'networks': [net_id], 'routers': [], 'ports': []}} mocked_sendjson.assert_called_once_with(mock.ANY, mock.ANY, formatted_bgpvpn) def test_odl_associate_router(self): mocked_sendjson = self.mocked_odlclient.sendjson with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] rt = bgpvpn['bgpvpn']['route_targets'] mocked_sendjson.reset_mock() with self.assoc_router(id, router_id): formatted_bgpvpn = { 'bgpvpn': {'export_targets': mock.ANY, 'name': mock.ANY, 'route_targets': rt, 'tenant_id': mock.ANY, 'project_id': mock.ANY, 'import_targets': mock.ANY, 'route_distinguishers': mock.ANY, 'type': mock.ANY, 'id': id, 'networks': [], 'routers': [router_id], 'ports': []}} mocked_sendjson.assert_called_once_with(mock.ANY, mock.ANY, formatted_bgpvpn) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/test_plugin.py0000666000175100017510000017462413245511271027663 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 contextlib import copy import mock import webob.exc from neutron_lib.plugins import directory from oslo_utils import uuidutils from neutron.api import extensions as api_extensions from neutron.db import servicetype_db as sdb from neutron import extensions as n_extensions from neutron.tests.unit.db import test_db_base_plugin_v2 from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit.extensions.test_l3 import TestL3NatServicePlugin from neutron_lib.api.definitions import bgpvpn as bgpvpn_def from neutron_lib.api.definitions import bgpvpn_vni as bgpvpn_vni_def from networking_bgpvpn.neutron.db import bgpvpn_db from networking_bgpvpn.neutron import extensions from networking_bgpvpn.neutron.services.common import constants from networking_bgpvpn.neutron.services import plugin from networking_bgpvpn.neutron.services.service_drivers import driver_api _uuid = uuidutils.generate_uuid def http_client_error(req, res): explanation = "Request '%s %s %s' failed: %s" % (req.method, req.url, req.body, res.body) return webob.exc.HTTPClientError(code=res.status_int, explanation=explanation) class TestBgpvpnDriverWithVni(driver_api.BGPVPNDriverRC): more_supported_extension_aliases = ( driver_api.BGPVPNDriverRC.more_supported_extension_aliases + [bgpvpn_vni_def.ALIAS]) def __init__(self, *args, **kwargs): super(TestBgpvpnDriverWithVni, self).__init__(*args, **kwargs) class BgpvpnTestCaseMixin(test_db_base_plugin_v2.NeutronDbPluginV2TestCase, test_l3.L3NatTestCaseMixin): def setUp(self, service_provider=None, core_plugin=None): if not service_provider: provider = (bgpvpn_def.LABEL + ':dummy:networking_bgpvpn.neutron.services.' 'service_drivers.driver_api.BGPVPNDriverRC:default') else: provider = (bgpvpn_def.LABEL + ':test:' + service_provider + ':default') bits = provider.split(':') provider = { 'service_type': bits[0], 'name': bits[1], 'driver': bits[2] } if len(bits) == 4: provider['default'] = True # override the default service provider self.service_providers = ( mock.patch.object(sdb.ServiceTypeManager, 'get_service_providers').start()) self.service_providers.return_value = [provider] bgpvpn_plugin_str = ('networking_bgpvpn.neutron.services.plugin.' 'BGPVPNPlugin') l3_plugin_str = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatServicePlugin') service_plugins = {'bgpvpn_plugin': bgpvpn_plugin_str, 'l3_plugin_name': l3_plugin_str} extensions_path = ':'.join(extensions.__path__ + n_extensions.__path__) # we need to provide a plugin instance, although # the extension manager will create a new instance # of the plugin ext_mgr = api_extensions.PluginAwareExtensionManager( extensions_path, {bgpvpn_def.LABEL: plugin.BGPVPNPlugin(), 'l3_plugin_name': TestL3NatServicePlugin()}) super(BgpvpnTestCaseMixin, self).setUp( plugin=core_plugin, service_plugins=service_plugins, ext_mgr=ext_mgr) # find the BGPVPN plugin that was instantiated by the # extension manager: self.bgpvpn_plugin = directory.get_plugin(bgpvpn_def.LABEL) self.bgpvpn_data = {'bgpvpn': {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'tenant_id': self._tenant_id}} self.converted_data = copy.copy(self.bgpvpn_data) self.converted_data['bgpvpn'].update({'export_targets': [], 'import_targets': [], 'route_distinguishers': []}) def add_tenant(self, data): data.update({ "project_id": self._tenant_id, "tenant_id": self._tenant_id }) @contextlib.contextmanager def bgpvpn(self, do_delete=True, **kwargs): req_data = copy.deepcopy(self.bgpvpn_data) fmt = 'json' if kwargs.get('data'): req_data = kwargs.get('data') else: req_data['bgpvpn'].update(kwargs) req = self.new_create_request( 'bgpvpn/bgpvpns', req_data, fmt=fmt) res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) bgpvpn = self.deserialize('json', res) yield bgpvpn if do_delete: self._delete('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) @contextlib.contextmanager def assoc_net(self, bgpvpn_id, net_id, do_disassociate=True): fmt = 'json' data = {'network_association': {'network_id': net_id, 'tenant_id': self._tenant_id}} req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=fmt, id=bgpvpn_id, subresource='network_associations') res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) assoc = self.deserialize('json', res) yield assoc if do_disassociate: del_req = self.new_delete_request( 'bgpvpn/bgpvpns', bgpvpn_id, fmt=self.fmt, subresource='network_associations', sub_id=assoc['network_association']['id']) res = del_req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(del_req, res) @contextlib.contextmanager def assoc_router(self, bgpvpn_id, router_id, do_disassociate=True): fmt = 'json' data = {'router_association': {'router_id': router_id, 'tenant_id': self._tenant_id}} req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=fmt, id=bgpvpn_id, subresource='router_associations') res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) assoc = self.deserialize('json', res) yield assoc if do_disassociate: del_req = self.new_delete_request( 'bgpvpn/bgpvpns', bgpvpn_id, fmt=self.fmt, subresource='router_associations', sub_id=assoc['router_association']['id']) res = del_req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(del_req, res) @contextlib.contextmanager def assoc_port(self, bgpvpn_id, port_id, do_disassociate=True, **kwargs): fmt = 'json' data = {'port_association': {'port_id': port_id, 'tenant_id': self._tenant_id}} data['port_association'].update(kwargs) req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=fmt, id=bgpvpn_id, subresource='port_associations') res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) assoc = self.deserialize('json', res) yield assoc if do_disassociate: del_req = self.new_delete_request( 'bgpvpn/bgpvpns', bgpvpn_id, fmt=self.fmt, subresource='port_associations', sub_id=assoc['port_association']['id']) res = del_req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(del_req, res) def show_port_assoc(self, bgpvpn_id, port_assoc_id): req = self.new_show_request("bgpvpn/bgpvpns", bgpvpn_id, subresource="port_associations", sub_id=port_assoc_id) res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) return self.deserialize('json', res) class TestBGPVPNServicePlugin(BgpvpnTestCaseMixin): def test_bgpvpn_net_assoc_create(self): with self.network() as net, \ self.bgpvpn() as bgpvpn, \ mock.patch.object( self.bgpvpn_plugin, '_validate_network', return_value=net['network']) as mock_validate, \ self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id']): mock_validate.assert_called_once_with( mock.ANY, net['network']['id']) def test_associate_empty_network(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) def test_associate_unknown_network(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] net_id = _uuid() data = {'network_association': {'network_id': net_id, 'tenant_id': self._tenant_id}} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPNotFound.code) def test_associate_unauthorized_net(self): with self.network() as net: net_id = net['network']['id'] with self.bgpvpn(tenant_id='another_tenant') as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'network_association': {'network_id': net_id, 'tenant_id': self._tenant_id}} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) def test_net_assoc_belong_to_diff_tenant(self): with self.network() as net: net_id = net['network']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'network_association': {'network_id': net_id, 'tenant_id': 'another_tenant'}} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) def test_bgpvpn_router_assoc_create(self): with self.router(tenant_id=self._tenant_id) as router,\ self.bgpvpn() as bgpvpn, \ mock.patch.object( self.bgpvpn_plugin, '_validate_router', return_value=router['router']) as mock_validate, \ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']): mock_validate.assert_called_once_with( mock.ANY, router['router']['id']) def test_bgpvpn_router_assoc_update(self): with self.router(tenant_id=self._tenant_id) as router, \ self.bgpvpn() as bgpvpn, \ mock.patch.object( self.bgpvpn_plugin, '_validate_router', return_value=router['router']), \ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']) as router_assoc: bgpvpn_id = bgpvpn['bgpvpn']['id'] updated = self._update('bgpvpn/bgpvpns/%s/router_associations' % bgpvpn_id, router_assoc['router_association']['id'], {'router_association': {'advertise_extra_routes': False}} ) expected = {'router_association': { 'id': router_assoc['router_association']['id'], 'router_id': router['router']['id'], 'advertise_extra_routes': False }} self.add_tenant(expected['router_association']) self.assertEqual(expected, updated) def test_associate_empty_router(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) def test_associate_unknown_router(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] router_id = _uuid() data = {'router_association': {'router_id': router_id, 'tenant_id': self._tenant_id}} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPNotFound.code) def test_associate_unauthorized_router(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn(tenant_id='another_tenant') as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'router_association': {'router_id': router_id, 'tenant_id': self._tenant_id}} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) def test_associate_router_incorrect_bgpvpn_type(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn(tenant_id='another_tenant', type=constants.BGPVPN_L2) as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'router_association': {'router_id': router_id, 'tenant_id': self._tenant_id}} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) def test_router_assoc_belong_to_diff_tenant(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'router_association': {'router_id': router_id, 'tenant_id': 'another_tenant'}} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) def test_router_net_combination(self): with self.network() as net,\ self.bgpvpn() as bgpvpn, \ self.router(tenant_id=self._tenant_id) as router: self._test_router_net_combination_validation( net['network'], router['router'], bgpvpn['bgpvpn']) with self.network() as net, \ self.bgpvpn() as bgpvpn, \ self.router(tenant_id=self._tenant_id) as router: self._test_net_router_combination_validation( net['network'], router['router'], bgpvpn['bgpvpn']) def _test_router_net_combination_validation(self, network, router, bgpvpn): net_id = network['id'] bgpvpn_id = bgpvpn['id'] router_id = router['id'] data = {'router_association': {'router_id': router_id, 'tenant_id': self._tenant_id}} req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn_id, subresource='router_associations') res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) with self.subnet(network={'network': network}) as subnet: data = {"subnet_id": subnet['subnet']['id']} bgpvpn_rtr_intf_req = self.new_update_request( 'routers', data=data, fmt=self.fmt, id=router['id'], subresource='add_router_interface') res = bgpvpn_rtr_intf_req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(bgpvpn_rtr_intf_req, res) data = {'network_association': {'network_id': net_id, 'tenant_id': self._tenant_id}} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn_id, subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) def _test_net_router_combination_validation(self, network, router, bgpvpn): net_id = network['id'] bgpvpn_id = bgpvpn['id'] router_id = router['id'] data = {'network_association': {'network_id': net_id, 'tenant_id': self._tenant_id}} req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn_id, subresource='network_associations') res = req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(req, res) with self.subnet(network={'network': network}) as subnet: data = {"subnet_id": subnet['subnet']['id']} bgpvpn_rtr_intf_req = self.new_update_request( 'routers', data=data, fmt=self.fmt, id=router['id'], subresource='add_router_interface') res = bgpvpn_rtr_intf_req.get_response(self.ext_api) if res.status_int >= 400: raise http_client_error(bgpvpn_rtr_intf_req, res) data = {'router_association': {'router_id': router_id, 'tenant_id': self._tenant_id}} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn_id, subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) def test_attach_subnet_to_router_both_attached_to_bgpvpn(self): with self.network() as net,\ self.bgpvpn() as bgpvpn,\ self.router(tenant_id=self._tenant_id) as router,\ self.subnet(network={'network': net['network']}) as subnet,\ self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id']),\ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']): # Attach subnet to router data = {"subnet_id": subnet['subnet']['id']} bgpvpn_rtr_intf_req = self.new_update_request( 'routers', data=data, fmt=self.fmt, id=router['router']['id'], subresource='add_router_interface') res = bgpvpn_rtr_intf_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPConflict.code) def test_attach_port_to_router_both_attached_to_bgpvpn(self): with self.network() as net,\ self.bgpvpn() as bgpvpn,\ self.router(tenant_id=self._tenant_id) as router,\ self.subnet(network={'network': net['network']}) as subnet,\ self.port(subnet={'subnet': subnet['subnet']}) as port,\ self.assoc_net(bgpvpn['bgpvpn']['id'], net['network']['id']),\ self.assoc_router(bgpvpn['bgpvpn']['id'], router['router']['id']): # Attach subnet to router data = {"port_id": port['port']['id']} bgpvpn_rtr_intf_req = self.new_update_request( 'routers', data=data, fmt=self.fmt, id=router['router']['id'], subresource='add_router_interface') res = bgpvpn_rtr_intf_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPConflict.code) @mock.patch.object(plugin.BGPVPNPlugin, '_validate_port_association_routes_bgpvpn') def test_bgpvpn_port_assoc_create(self, mock_validate_port_assoc): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn, \ mock.patch.object( self.bgpvpn_plugin, '_validate_port', return_value=port['port']) as mock_validate, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id'], advertise_fixed_ips=False, routes=[{ 'type': 'prefix', 'prefix': '12.1.3.0/24', }]): mock_validate.assert_called_once_with( mock.ANY, port['port']['id']) mock_validate_port_assoc.assert_called_once() def _test_bgpvpn_port_assoc_create_incorrect(self, **kwargs): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn: data = {'port_association': {'port_id': port['port']['id'], 'tenant_id': self._tenant_id}} data['port_association'].update(kwargs) bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn['bgpvpn']['id'], subresource='port_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) return res.body def test_bgpvpn_port_assoc_create_bgpvpn_route_non_existing(self): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn: data = {'port_association': { 'port_id': port['port']['id'], 'tenant_id': self._tenant_id, 'routes': [{ 'type': 'bgpvpn', 'bgpvpn_id': '3aff9b6b-387b-4ffd-a9ff-a4bdffb349ff' }] }} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn['bgpvpn']['id'], subresource='port_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) self.assertIn("bgpvpn specified in route does not exist", str(res.body)) def test_bgpvpn_port_assoc_create_bgpvpn_route_wrong_tenant(self): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn, \ self.bgpvpn(tenant_id="notus") as bgpvpn_other: data = {'port_association': { 'port_id': port['port']['id'], 'tenant_id': self._tenant_id, 'routes': [{ 'type': 'bgpvpn', 'bgpvpn_id': bgpvpn_other['bgpvpn']['id'] }] }} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=bgpvpn['bgpvpn']['id'], subresource='port_associations') res = bgpvpn_net_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) self.assertIn("bgpvpn specified in route does not belong to " "the tenant", str(res.body)) def test_associate_empty_port(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {} bgpvpn_port_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='port_associations') res = bgpvpn_port_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) self.assertIn("Resource body required", str(res.body)) def test_associate_unknown_port(self): with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] port_id = _uuid() data = {'port_association': {'port_id': port_id, 'tenant_id': self._tenant_id}} bgpvpn_port_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='port_associations') res = bgpvpn_port_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPNotFound.code) def test_associate_unauthorized_port(self): with self.port(tenant_id=self._tenant_id) as port: port_id = port['port']['id'] with self.bgpvpn(tenant_id='another_tenant') as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'port_association': {'port_id': port_id, 'tenant_id': self._tenant_id}} bgpvpn_port_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='port_associations') res = bgpvpn_port_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) def test_port_assoc_belong_to_diff_tenant(self): with self.port(tenant_id=self._tenant_id) as port: port_id = port['port']['id'] with self.bgpvpn() as bgpvpn: id = bgpvpn['bgpvpn']['id'] data = {'port_association': {'port_id': port_id, 'tenant_id': 'another_tenant'}} bgpvpn_port_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=self.fmt, id=id, subresource='port_associations') res = bgpvpn_port_req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPForbidden.code) @mock.patch.object(plugin.BGPVPNPlugin, '_validate_port_association_routes_bgpvpn') def test_bgpvpn_port_assoc_update( self, mock_validate): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as port_assoc: bgpvpn_id = bgpvpn['bgpvpn']['id'] self._update('bgpvpn/bgpvpns/%s/port_associations' % bgpvpn_id, port_assoc['port_association']['id'], {'port_association': {'advertise_fixed_ips': False}} ) # one call for create, one call for update self.assertEqual(2, mock_validate.call_count) def test_bgpvpn_port_assoc_update_bgpvpn_route_wrong_tenant(self): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn() as bgpvpn, \ self.bgpvpn(tenant_id="not-us") as bgpvpn_other, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as port_assoc: bgpvpn_id = bgpvpn['bgpvpn']['id'] req = self.new_update_request( 'bgpvpn/bgpvpns/%s/port_associations' % bgpvpn_id, {'port_association': { 'routes': [{ 'type': 'bgpvpn', 'bgpvpn_id': bgpvpn_other['bgpvpn']['id'] }] } }, port_assoc['port_association']['id'] ) res = req.get_response(self.ext_api) self.assertEqual(res.status_int, webob.exc.HTTPBadRequest.code) self.assertIn( "bgpvpn specified in route does not belong to the tenant", str(res.body)) class TestBGPVPNServiceDriverDB(BgpvpnTestCaseMixin): def setUp(self): super(TestBGPVPNServiceDriverDB, self).setUp() def _raise_bgpvpn_driver_precommit_exc(self, *args, **kwargs): raise extensions.bgpvpn.BGPVPNDriverError( method='precommit method') @mock.patch.object(driver_api.BGPVPNDriver, 'create_bgpvpn_postcommit') @mock.patch.object(driver_api.BGPVPNDriver, 'create_bgpvpn_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'create_bgpvpn') def test_create_bgpvpn(self, mock_create_db, mock_create_precommit, mock_create_postcommit): mock_create_db.return_value = self.converted_data['bgpvpn'] with self.bgpvpn(do_delete=False): self.assertTrue(mock_create_db.called) self.assertDictSupersetOf( self.converted_data['bgpvpn'], mock_create_db.call_args[0][1]) mock_create_precommit.assert_called_once_with( mock.ANY, self.converted_data['bgpvpn']) mock_create_postcommit.assert_called_once_with( mock.ANY, self.converted_data['bgpvpn']) def test_create_bgpvpn_precommit_fails(self): with mock.patch.object(driver_api.BGPVPNDriver, 'create_bgpvpn_precommit', new=self._raise_bgpvpn_driver_precommit_exc): # Assert that an error is returned to the client bgpvpn_req = self.new_create_request( 'bgpvpn/bgpvpns', self.bgpvpn_data) res = bgpvpn_req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPError.code, res.status_int) # Assert that no bgpvpn has been created list = self._list('bgpvpn/bgpvpns', fmt='json') self.assertEqual([], list['bgpvpns']) def test_delete_bgpvpn_precommit_fails(self): with self.bgpvpn(do_delete=False) as bgpvpn, \ mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_bgpvpn', return_value=self.converted_data), \ mock.patch.object(driver_api.BGPVPNDriver, 'delete_bgpvpn_precommit', new=self._raise_bgpvpn_driver_precommit_exc): bgpvpn_req = self.new_delete_request('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) res = bgpvpn_req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPError.code, res.status_int) # Assert that existing bgpvpn remains list = self._list('bgpvpn/bgpvpns', fmt='json') self.assertEqual([bgpvpn['bgpvpn']], list['bgpvpns']) @mock.patch.object(driver_api.BGPVPNDriver, 'delete_bgpvpn_postcommit') def test_delete_bgpvpn(self, mock_delete_postcommit): with self.bgpvpn(do_delete=False) as bgpvpn, \ mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_bgpvpn') \ as mock_delete_db, \ mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_bgpvpn', return_value=self.converted_data): self._delete('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) mock_delete_db.assert_called_once_with(mock.ANY, bgpvpn['bgpvpn']['id']) mock_delete_postcommit.assert_called_once_with(mock.ANY, self.converted_data) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_bgpvpn') def test_get_bgpvpn(self, mock_get_db): with self.bgpvpn() as bgpvpn: self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) mock_get_db.assert_called_once_with(mock.ANY, bgpvpn['bgpvpn']['id'], mock.ANY) def test_get_bgpvpn_with_net(self): with self.network() as net: net_id = net['network']['id'] with self.bgpvpn() as bgpvpn: with self.assoc_net(bgpvpn['bgpvpn']['id'], net_id=net_id): res = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertIn('networks', res['bgpvpn']) self.assertEqual(net_id, res['bgpvpn']['networks'][0]) def test_get_bgpvpn_with_router(self): with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.bgpvpn() as bgpvpn: with self.assoc_router(bgpvpn['bgpvpn']['id'], router_id): res = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertIn('routers', res['bgpvpn']) self.assertEqual(router_id, res['bgpvpn']['routers'][0]) @mock.patch.object(driver_api.BGPVPNDriver, 'update_bgpvpn_postcommit') @mock.patch.object(driver_api.BGPVPNDriver, 'update_bgpvpn_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'update_bgpvpn') def test_update_bgpvpn(self, mock_update_db, mock_update_precommit, mock_update_postcommit): with self.bgpvpn() as bgpvpn: old_bgpvpn = copy.copy(self.bgpvpn_data['bgpvpn']) old_bgpvpn['id'] = bgpvpn['bgpvpn']['id'] old_bgpvpn['networks'] = [] old_bgpvpn['routers'] = [] old_bgpvpn['ports'] = [] old_bgpvpn['project_id'] = old_bgpvpn['tenant_id'] old_bgpvpn['local_pref'] = None new_bgpvpn = copy.copy(old_bgpvpn) update = {'name': 'foo'} new_bgpvpn.update(update) mock_update_db.return_value = new_bgpvpn data = {"bgpvpn": {"name": new_bgpvpn['name']}} self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], data) mock_update_db.assert_called_once_with( mock.ANY, bgpvpn['bgpvpn']['id'], data['bgpvpn']) mock_update_precommit.assert_called_once_with( mock.ANY, old_bgpvpn, new_bgpvpn) mock_update_postcommit.assert_called_once_with( mock.ANY, old_bgpvpn, new_bgpvpn) def test_update_bgpvpn_precommit_fails(self): with self.bgpvpn() as bgpvpn, \ mock.patch.object(driver_api.BGPVPNDriver, 'update_bgpvpn_precommit', new=self._raise_bgpvpn_driver_precommit_exc): new_data = {"bgpvpn": {"name": "foo"}} self._update('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id'], new_data, expected_code=webob.exc.HTTPError.code) show_bgpvpn = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertEqual(self.bgpvpn_data['bgpvpn']['name'], show_bgpvpn['bgpvpn']['name']) @mock.patch.object(driver_api.BGPVPNDriver, 'create_net_assoc_postcommit') @mock.patch.object(driver_api.BGPVPNDriver, 'create_net_assoc_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'create_net_assoc') def test_create_bgpvpn_net_assoc(self, mock_db_create_assoc, mock_pre_commit, mock_post_commit): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.network() as net: net_id = net['network']['id'] assoc_id = _uuid() data = {'tenant_id': self._tenant_id, 'network_id': net_id} net_assoc_dict = copy.copy(data) net_assoc_dict.update({'id': assoc_id, 'bgpvpn_id': bgpvpn_id}) mock_db_create_assoc.return_value = net_assoc_dict with self.assoc_net(bgpvpn_id, net_id=net_id, do_disassociate=False): self.assertTrue(mock_db_create_assoc.called) self.assertEqual( bgpvpn_id, mock_db_create_assoc.call_args[0][1]) self.assertDictSupersetOf( data, mock_db_create_assoc.call_args[0][2]) mock_pre_commit.assert_called_once_with(mock.ANY, net_assoc_dict) mock_post_commit.assert_called_once_with(mock.ANY, net_assoc_dict) def test_create_bgpvpn_net_assoc_precommit_fails(self): with self.bgpvpn() as bgpvpn, \ self.network() as net, \ mock.patch.object(driver_api.BGPVPNDriver, 'create_net_assoc_precommit', new=self._raise_bgpvpn_driver_precommit_exc): fmt = 'json' data = {'network_association': {'network_id': net['network']['id'], 'tenant_id': self._tenant_id}} bgpvpn_net_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=fmt, id=bgpvpn['bgpvpn']['id'], subresource='network_associations') res = bgpvpn_net_req.get_response(self.ext_api) # Assert that driver failure returns an error self.assertEqual(webob.exc.HTTPError.code, res.status_int) # Assert that the bgpvpn is not associated to network bgpvpn_new = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertEqual([], bgpvpn_new['bgpvpn']['networks']) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_net_assoc') def test_get_bgpvpn_net_assoc(self, mock_get_db): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.network() as net: net_id = net['network']['id'] with self.assoc_net(bgpvpn_id, net_id=net_id) as assoc: assoc_id = assoc['network_association']['id'] res = 'bgpvpn/bgpvpns/' + bgpvpn_id + \ '/network_associations' self._show(res, assoc_id) mock_get_db.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id, []) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_net_assocs') def test_get_bgpvpn_net_assoc_list(self, mock_get_db): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.network() as net: net_id = net['network']['id'] with self.assoc_net(bgpvpn_id, net_id=net_id): res = 'bgpvpn/bgpvpns/' + bgpvpn_id + \ '/network_associations' self._list(res) mock_get_db.assert_called_once_with(mock.ANY, bgpvpn_id, mock.ANY, mock.ANY) @mock.patch.object(driver_api.BGPVPNDriver, 'delete_net_assoc_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_net_assoc') def test_delete_bgpvpn_net_assoc_precommit_fails(self, mock_db_del, mock_precommit): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.network() as net: net_id = net['network']['id'] with self.assoc_net(bgpvpn_id, net_id=net_id) as assoc: assoc_id = assoc['network_association']['id'] net_assoc = {'id': assoc_id, 'network_id': net_id, 'bgpvpn_id': bgpvpn_id} mock_db_del.return_value = net_assoc mock_precommit.return_value = \ self._raise_bgpvpn_driver_precommit_exc # Assert that existing bgpvpn and net-assoc remains list = self._list('bgpvpn/bgpvpns', fmt='json') bgpvpn['bgpvpn']['networks'] = [net_assoc['network_id']] self.assertEqual([bgpvpn['bgpvpn']], list['bgpvpns']) @mock.patch.object(driver_api.BGPVPNDriver, 'delete_net_assoc_postcommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_net_assoc') def test_delete_bgpvpn_net_assoc(self, mock_db_del, mock_postcommit): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.network() as net: net_id = net['network']['id'] with self.assoc_net(bgpvpn_id, net_id=net_id) as assoc: assoc_id = assoc['network_association']['id'] net_assoc = {'id': assoc_id, 'network_id': net_id, 'bgpvpn_id': bgpvpn_id} self.add_tenant(net_assoc) mock_db_del.return_value = net_assoc mock_db_del.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id) mock_postcommit.assert_called_once_with(mock.ANY, net_assoc) @mock.patch.object(driver_api.BGPVPNDriver, 'create_router_assoc_postcommit') @mock.patch.object(driver_api.BGPVPNDriver, 'create_router_assoc_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'create_router_assoc') def test_create_bgpvpn_router_assoc(self, mock_db_create_assoc, mock_pre_commit, mock_post_commit): with self.bgpvpn() as bgpvpn, \ self.router(tenant_id=self._tenant_id) as router: bgpvpn_id = bgpvpn['bgpvpn']['id'] router_id = router['router']['id'] assoc_id = _uuid() data = {'tenant_id': self._tenant_id, 'router_id': router_id} router_assoc_dict = copy.copy(data) router_assoc_dict.update({'id': assoc_id, 'bgpvpn_id': bgpvpn_id}) mock_db_create_assoc.return_value = router_assoc_dict with self.assoc_router(bgpvpn_id, router_id=router_id, do_disassociate=False): self.assertTrue(mock_db_create_assoc.called) self.assertEqual( bgpvpn_id, mock_db_create_assoc.call_args[0][1]) self.assertDictSupersetOf( data, mock_db_create_assoc.call_args[0][2]) mock_pre_commit.assert_called_once_with(mock.ANY, router_assoc_dict) mock_post_commit.assert_called_once_with(mock.ANY, router_assoc_dict) def test_create_bgpvpn_router_assoc_precommit_fails(self): with self.bgpvpn() as bgpvpn, \ self.router(tenant_id=self._tenant_id) as router, \ mock.patch.object(driver_api.BGPVPNDriver, 'create_router_assoc_precommit', new=self._raise_bgpvpn_driver_precommit_exc): fmt = 'json' data = {'router_association': {'router_id': router['router']['id'], 'tenant_id': self._tenant_id}} bgpvpn_router_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=fmt, id=bgpvpn['bgpvpn']['id'], subresource='router_associations') res = bgpvpn_router_req.get_response(self.ext_api) # Assert that driver failure returns an error self.assertEqual(webob.exc.HTTPError.code, res.status_int) # Assert that the bgpvpn is not associated to network bgpvpn_new = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertEqual([], bgpvpn_new['bgpvpn']['routers']) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_router_assoc') def test_get_bgpvpn_router_assoc(self, mock_get_db): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.assoc_router(bgpvpn_id, router_id) as assoc: assoc_id = assoc['router_association']['id'] res = 'bgpvpn/bgpvpns/' + bgpvpn_id + \ '/router_associations' self._show(res, assoc_id) mock_get_db.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id, []) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_router_assocs') def test_get_bgpvpn_router_assoc_list(self, mock_get_db): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.assoc_router(bgpvpn_id, router_id): res = 'bgpvpn/bgpvpns/' + bgpvpn_id + \ '/router_associations' self._list(res) mock_get_db.assert_called_once_with(mock.ANY, bgpvpn_id, mock.ANY, mock.ANY) @mock.patch.object(driver_api.BGPVPNDriver, 'delete_router_assoc_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_router_assoc') def test_delete_bgpvpn_router_assoc_precommit_fails(self, mock_db_del, mock_precommit): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.assoc_router(bgpvpn_id, router_id) as assoc: assoc_id = assoc['router_association']['id'] router_assoc = {'id': assoc_id, 'router_id': router_id, 'bgpvpn_id': bgpvpn_id} mock_db_del.return_value = router_assoc mock_precommit.return_value = \ self._raise_bgpvpn_driver_precommit_exc # Assert that existing bgpvpn and router-assoc remains list = self._list('bgpvpn/bgpvpns', fmt='json') bgpvpn['bgpvpn']['routers'] = [router_assoc['router_id']] self.assertEqual([bgpvpn['bgpvpn']], list['bgpvpns']) @mock.patch.object(driver_api.BGPVPNDriver, 'delete_router_assoc_postcommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_router_assoc') def test_delete_bgpvpn_router_assoc(self, mock_db_del, mock_postcommit): with self.bgpvpn() as bgpvpn: bgpvpn_id = bgpvpn['bgpvpn']['id'] with self.router(tenant_id=self._tenant_id) as router: router_id = router['router']['id'] with self.assoc_router(bgpvpn_id, router_id) as assoc: assoc_id = assoc['router_association']['id'] router_assoc = {'id': assoc_id, 'router_id': router_id, 'bgpvpn_id': bgpvpn_id, 'advertise_extra_routes': True} self.add_tenant(router_assoc) mock_db_del.return_value = router_assoc # (delete triggered by exit from with statement) mock_db_del.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id) mock_postcommit.assert_called_once_with(mock.ANY, router_assoc) @mock.patch.object(driver_api.BGPVPNDriverRC, 'create_port_assoc_postcommit') @mock.patch.object(driver_api.BGPVPNDriverRC, 'create_port_assoc_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'create_port_assoc') def test_create_bgpvpn_port_assoc(self, mock_db_create_assoc, mock_pre_commit, mock_post_commit): with self.bgpvpn() as bgpvpn, \ self.network() as net,\ self.subnet(network={'network': net['network']}) as subnet,\ self.port(subnet={'subnet': subnet['subnet']}, tenant_id=self._tenant_id) as port: bgpvpn_id = bgpvpn['bgpvpn']['id'] port_id = port['port']['id'] assoc_id = _uuid() data = {'tenant_id': self._tenant_id, 'port_id': port_id} port_assoc_dict = copy.copy(data) port_assoc_dict.update({'id': assoc_id, 'bgpvpn_id': bgpvpn_id}) mock_db_create_assoc.return_value = port_assoc_dict with self.assoc_port(bgpvpn_id, port_id=port_id, do_disassociate=False): self.assertTrue(mock_db_create_assoc.called) self.assertEqual( bgpvpn_id, mock_db_create_assoc.call_args[0][1]) self.assertDictSupersetOf( data, mock_db_create_assoc.call_args[0][2]) mock_pre_commit.assert_called_once_with(mock.ANY, port_assoc_dict) mock_post_commit.assert_called_once_with(mock.ANY, port_assoc_dict) def test_create_bgpvpn_port_assoc_precommit_fails(self): with self.bgpvpn() as bgpvpn, \ self.port(tenant_id=self._tenant_id) as port, \ mock.patch.object(driver_api.BGPVPNDriverRC, 'create_port_assoc_precommit', new=self._raise_bgpvpn_driver_precommit_exc): fmt = 'json' data = {'port_association': {'port_id': port['port']['id'], 'tenant_id': self._tenant_id}} bgpvpn_port_req = self.new_create_request( 'bgpvpn/bgpvpns', data=data, fmt=fmt, id=bgpvpn['bgpvpn']['id'], subresource='port_associations') res = bgpvpn_port_req.get_response(self.ext_api) # Assert that driver failure returns an error self.assertEqual(webob.exc.HTTPError.code, res.status_int) # Assert that the bgpvpn is not associated to network bgpvpn_new = self._show('bgpvpn/bgpvpns', bgpvpn['bgpvpn']['id']) self.assertEqual([], bgpvpn_new['bgpvpn']['ports']) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_port_assoc') def test_get_bgpvpn_port_assoc(self, mock_get_db): with self.bgpvpn() as bgpvpn, \ self.port(tenant_id=self._tenant_id) as port, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as assoc: bgpvpn_id = bgpvpn['bgpvpn']['id'] assoc_id = assoc['port_association']['id'] res = 'bgpvpn/bgpvpns/' + bgpvpn_id + \ '/port_associations' self._show(res, assoc_id) mock_get_db.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id, []) @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'get_port_assocs') def test_get_bgpvpn_port_assoc_list(self, mock_get_db): with self.bgpvpn() as bgpvpn, \ self.port(tenant_id=self._tenant_id) as port, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']): bgpvpn_id = bgpvpn['bgpvpn']['id'] res = 'bgpvpn/bgpvpns/' + bgpvpn_id + \ '/port_associations' self._list(res) mock_get_db.assert_called_once_with(mock.ANY, bgpvpn_id, mock.ANY, mock.ANY) @mock.patch.object(driver_api.BGPVPNDriverRC, 'delete_port_assoc_precommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_port_assoc') def test_delete_bgpvpn_port_assoc_precommit_fails(self, mock_db_del, mock_precommit): with self.bgpvpn() as bgpvpn, \ self.port(tenant_id=self._tenant_id) as port, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as assoc: port_id = port['port']['id'] bgpvpn_id = bgpvpn['bgpvpn']['id'] assoc_id = assoc['port_association']['id'] port_assoc = {'id': assoc_id, 'port_id': port_id, 'bgpvpn_id': bgpvpn_id} mock_db_del.return_value = port_assoc mock_precommit.return_value = \ self._raise_bgpvpn_driver_precommit_exc # Assert that existing bgpvpn and port-assoc remains list = self._list('bgpvpn/bgpvpns', fmt='json') bgpvpn['bgpvpn']['ports'] = [port_assoc['port_id']] self.assertEqual([bgpvpn['bgpvpn']], list['bgpvpns']) @mock.patch.object(driver_api.BGPVPNDriverRC, 'update_port_assoc_precommit') @mock.patch.object(driver_api.BGPVPNDriverRC, 'update_port_assoc_postcommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'update_port_assoc') def test_update_bgpvpn_port_assoc(self, mock_db_update, mock_postcommit, mock_precommit): with self.bgpvpn() as bgpvpn, \ self.port(tenant_id=self._tenant_id) as port, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as assoc: bgpvpn_id = bgpvpn['bgpvpn']['id'] assoc_id = assoc['port_association']['id'] assoc['port_association'].update({'bgpvpn_id': bgpvpn_id}) new_port_assoc = copy.deepcopy(assoc) changed = {'advertise_fixed_ips': False} new_port_assoc['port_association'].update(changed) mock_db_update.return_value = new_port_assoc['port_association'] data = {"port_association": changed} self._update('bgpvpn/bgpvpns/%s/port_associations' % bgpvpn_id, assoc['port_association']['id'], data) mock_db_update.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id, data['port_association']) mock_precommit.assert_called_once_with( mock.ANY, assoc['port_association'], new_port_assoc['port_association'] ) mock_postcommit.assert_called_once_with( mock.ANY, assoc['port_association'], new_port_assoc['port_association'] ) @mock.patch.object(driver_api.BGPVPNDriverRC, 'delete_port_assoc_precommit') @mock.patch.object(driver_api.BGPVPNDriverRC, 'delete_port_assoc_postcommit') @mock.patch.object(bgpvpn_db.BGPVPNPluginDb, 'delete_port_assoc') def test_delete_bgpvpn_port_assoc(self, mock_db_del, mock_postcommit, mock_precommit): with self.bgpvpn() as bgpvpn, \ self.port(tenant_id=self._tenant_id) as port, \ self.assoc_port(bgpvpn['bgpvpn']['id'], port['port']['id']) as assoc: port_id = port['port']['id'] bgpvpn_id = bgpvpn['bgpvpn']['id'] assoc_id = assoc['port_association']['id'] port_assoc = {'id': assoc_id, 'bgpvpn_id': bgpvpn_id, 'port_id': port_id, 'routes': [], 'advertise_fixed_ips': True} self.add_tenant(port_assoc) mock_db_del.return_value = port_assoc # (delete triggered by exit from with statement) mock_db_del.assert_called_once_with(mock.ANY, assoc_id, bgpvpn_id) mock_precommit.assert_called_once_with(mock.ANY, port_assoc) mock_postcommit.assert_called_once_with(mock.ANY, port_assoc) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/services/__init__.py0000666000175100017510000000000013245511235027035 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/extensions/0000775000175100017510000000000013245511747025320 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn.py0000666000175100017510000003657713245511235030241 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 copy import mock from oslo_utils import uuidutils from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.extensions import base as test_extensions_base from neutron_lib.api.definitions import bgpvpn as bgpvpn_api_def from webob import exc from networking_bgpvpn.neutron.extensions import bgpvpn _uuid = uuidutils.generate_uuid _get_path = test_base._get_path BGPVPN_PREFIX = 'bgpvpn' BGPVPN_URI = BGPVPN_PREFIX + '/' + 'bgpvpns' class BgpvpnExtensionTestCaseBase(test_extensions_base.ExtensionTestCase): def setUp(self): super(BgpvpnExtensionTestCaseBase, self).setUp() plural_mappings = {'bgpvpn': 'bgpvpns'} self._setUpExtension( '%s.%s' % (bgpvpn.BGPVPNPluginBase.__module__, bgpvpn.BGPVPNPluginBase.__name__), bgpvpn_api_def.LABEL, None, bgpvpn.Bgpvpn, BGPVPN_PREFIX, plural_mappings=plural_mappings, translate_resource_name=True) self.instance = self.plugin.return_value self.bgpvpn_id = _uuid() self.net_id = _uuid() self.router_id = _uuid() self.net_assoc_id = _uuid() self.router_assoc_id = _uuid() self.NET_ASSOC_URI = BGPVPN_URI + '/' + self.bgpvpn_id + \ '/network_associations' self.ROUTER_ASSOC_URI = BGPVPN_URI + '/' + self.bgpvpn_id + \ '/router_associations' class BgpvpnExtensionTestCase(BgpvpnExtensionTestCaseBase): def test_bgpvpn_create(self): bgpvpn_id = _uuid() data = { 'bgpvpn': {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'tenant_id': _uuid()} } expected_ret_val = copy.copy(data['bgpvpn']) expected_ret_val['import_targets'] = [] expected_ret_val['export_targets'] = [] expected_ret_val['route_distinguishers'] = [] expected_call_args = copy.copy(expected_ret_val) expected_ret_val.update({'id': bgpvpn_id}) self.instance.create_bgpvpn.return_value = expected_ret_val res = self.api.post(_get_path(BGPVPN_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt) self.assertTrue(self.instance.create_bgpvpn.called) self.assertDictSupersetOf( expected_call_args, self.instance.create_bgpvpn.call_args[1]['bgpvpn']['bgpvpn']) self.assertEqual(res.status_int, exc.HTTPCreated.code) res = self.deserialize(res) self.assertIn('bgpvpn', res) self.assertDictSupersetOf(expected_ret_val, res['bgpvpn']) def test_bgpvpn_create_with_malformatted_route_target(self): data = { 'bgpvpn': {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['ASN:NN'], 'tenant_id': _uuid()} } res = self.api.post(_get_path(BGPVPN_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertEqual(res.status_int, exc.HTTPBadRequest.code) def _data_for_invalid_rtdt(self, field): values = [[':1'], ['1:'], ['42'], ['65536:123456'], ['123.456.789.123:65535'], ['4294967296:65535'], ['1.1.1.1:655351'], ['4294967295:65536'], [''], ] for value in values: yield {'bgpvpn': {'name': 'bgpvpn1', 'type': 'l3', field: value, 'tenant_id': _uuid()} } def _data_for_valid_rtdt(self, field): values = [['1:1'], ['1:4294967295'], ['65535:0'], ['65535:4294967295'], ['1.1.1.1:1'], ['1.1.1.1:65535'], ['4294967295:0'], ['65536:65535'], ['4294967295:65535'], ] for value in values: yield {'bgpvpn': {'name': 'bgpvpn1', 'type': 'l3', field: value, 'tenant_id': _uuid()} } def _test_invalid_field(self, field): for data in self._data_for_invalid_rtdt(field): res = self.api.post(_get_path(BGPVPN_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertEqual(res.status_int, exc.HTTPBadRequest.code, "test failed for %s" % data) def _test_valid_field(self, field): for data in self._data_for_valid_rtdt(field): res = self.api.post(_get_path(BGPVPN_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=False) self.assertEqual(res.status_int, exc.HTTPCreated.code, "test failed for %s" % data) def test_bgpvpn_create_with_invalid_route_targets(self): self._test_invalid_field('route_targets') def test_bgpvpn_create_with_valid_route_targets(self): self._test_valid_field('route_targets') def test_bgpvpn_create_with_invalid_import_rts(self): self._test_invalid_field('import_targets') def test_bgpvpn_create_with_valid_import_rts(self): self._test_valid_field('import_targets') def test_bgpvpn_create_with_invalid_export_rts(self): self._test_invalid_field('export_targets') def test_bgpvpn_create_with_valid_export_rts(self): self._test_valid_field('export_targets') def test_bgpvpn_create_with_invalid_route_distinguishers(self): self._test_invalid_field('route_distinguishers') def test_bgpvpn_create_with_valid_route_distinguishers(self): self._test_valid_field('route_distinguishers') def test_bgpvpn_list(self): bgpvpn_id = _uuid() return_value = [{'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'id': bgpvpn_id}] self.instance.get_bgpvpns.return_value = return_value res = self.api.get( _get_path(BGPVPN_URI, fmt=self.fmt)) self.instance.get_bgpvpns.assert_called_with( mock.ANY, fields=mock.ANY, filters=mock.ANY ) self.assertEqual(res.status_int, exc.HTTPOk.code) def test_bgpvpn_update(self): bgpvpn_id = _uuid() update_data = {'bgpvpn': {'name': 'bgpvpn_updated'}} return_value = {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'tenant_id': _uuid(), 'id': bgpvpn_id} self.instance.update_bgpvpn.return_value = return_value res = self.api.put(_get_path(BGPVPN_URI, id=bgpvpn_id, fmt=self.fmt), self.serialize(update_data), content_type='application/%s' % self.fmt) self.instance.update_bgpvpn.assert_called_with( mock.ANY, bgpvpn_id, bgpvpn=update_data ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('bgpvpn', res) self.assertEqual(res['bgpvpn'], return_value) def test_bgpvpn_get(self): bgpvpn_id = _uuid() return_value = {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'tenant_id': _uuid(), 'id': bgpvpn_id} self.instance.get_bgpvpn.return_value = return_value res = self.api.get(_get_path(BGPVPN_URI, id=bgpvpn_id, fmt=self.fmt)) self.instance.get_bgpvpn.assert_called_with( mock.ANY, bgpvpn_id, fields=mock.ANY ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('bgpvpn', res) self.assertEqual(res['bgpvpn'], return_value) def test_bgpvpn_delete(self): self._test_entity_delete('bgpvpn') def test_bgpvpn_net_create(self): data = {'network_association': {'network_id': self.net_id, 'tenant_id': _uuid()}} return_value = copy.copy(data['network_association']) return_value.update({'id': self.net_assoc_id}) self.instance.create_bgpvpn_network_association.return_value = \ return_value res = self.api.post(_get_path(self.NET_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertTrue(self.instance.create_bgpvpn_network_association.called) self.assertEqual(self.bgpvpn_id, self.instance.create_bgpvpn_network_association. call_args[1]['bgpvpn_id']) self.assertDictSupersetOf( data['network_association'], self.instance.create_bgpvpn_network_association. call_args[1]['network_association']['network_association']) self.assertIn('network_association', res) res = self.deserialize(res) self.assertDictSupersetOf(return_value, res['network_association']) def _invalid_data_for_creation(self, target): return [None, {}, {target: None}, {target: {}}] def test_bgpvpn_net_create_with_invalid_data(self): for data in self._invalid_data_for_creation('network_association'): res = self.api.post(_get_path(self.NET_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertFalse( self.instance.create_bgpvpn_network_association.called) self.assertEqual(res.status_int, exc.HTTPBadRequest.code) def test_bgpvpn_net_get(self): return_value = {'id': self.net_assoc_id, 'network_id': self.net_id} self.instance.get_bgpvpn_network_association.return_value = \ return_value res = self.api.get(_get_path(self.NET_ASSOC_URI, id=self.net_assoc_id, fmt=self.fmt)) self.instance.get_bgpvpn_network_association.assert_called_with( mock.ANY, self.net_assoc_id, self.bgpvpn_id, fields=mock.ANY ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('network_association', res) self.assertEqual(return_value, res['network_association']) def test_bgpvpn_net_update(self): pass def test_bgpvpn_net_delete(self): res = self.api.delete(_get_path(self.NET_ASSOC_URI, id=self.net_assoc_id, fmt=self.fmt)) self.instance.delete_bgpvpn_network_association.assert_called_with( mock.ANY, self.net_assoc_id, self.bgpvpn_id) self.assertEqual(res.status_int, exc.HTTPNoContent.code) def test_bgpvpn_router_create(self): data = { 'router_association': { 'router_id': self.router_id, 'tenant_id': _uuid() } } return_value = copy.copy(data['router_association']) return_value.update({'id': self.router_assoc_id}) self.instance.create_bgpvpn_router_association.return_value = \ return_value res = self.api.post(_get_path(self.ROUTER_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertTrue(self.instance.create_bgpvpn_router_association.called) self.assertEqual(self.bgpvpn_id, self.instance.create_bgpvpn_router_association. call_args[1]['bgpvpn_id']) self.assertDictSupersetOf( data['router_association'], self.instance.create_bgpvpn_router_association. call_args[1]['router_association']['router_association']) self.assertIn('router_association', res) res = self.deserialize(res) self.assertDictSupersetOf(return_value, res['router_association']) def test_bgpvpn_router_create_with_invalid_data(self): for data in self._invalid_data_for_creation('router_association'): res = self.api.post(_get_path(self.ROUTER_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertFalse( self.instance.create_bgpvpn_router_association.called) self.assertEqual(res.status_int, exc.HTTPBadRequest.code) def test_bgpvpn_router_get(self): return_value = {'id': self.router_assoc_id, 'router_id': self.router_id} self.instance.get_bgpvpn_router_association.return_value = \ return_value res = self.api.get(_get_path(self.ROUTER_ASSOC_URI, id=self.router_assoc_id, fmt=self.fmt)) self.instance.get_bgpvpn_router_association.assert_called_with( mock.ANY, self.router_assoc_id, self.bgpvpn_id, fields=mock.ANY ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('router_association', res) self.assertEqual(return_value, res['router_association']) def test_bgpvpn_router_update(self): pass def test_bgpvpn_router_delete(self): res = self.api.delete(_get_path(self.ROUTER_ASSOC_URI, id=self.router_assoc_id, fmt=self.fmt)) self.instance.delete_bgpvpn_router_association.assert_called_with( mock.ANY, self.router_assoc_id, self.bgpvpn_id) self.assertEqual(res.status_int, exc.HTTPNoContent.code) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn_rc_base.py0000666000175100017510000001104713245511271031700 0ustar zuulzuul00000000000000# Copyright 2014 Intel Corporation. # Copyright 2014 Isaku Yamahata # # All Rights Reserved. # # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import mock from oslo_config import cfg import webtest from neutron_lib import fixture from neutron.api import extensions from neutron.api.v2 import attributes from neutron import manager from neutron import quota from neutron.tests.unit.api import test_extensions from neutron.tests.unit.extensions import base as test_extensions_base CORE_PLUGIN = 'neutron.db.db_base_plugin_v2.NeutronDbPluginV2' class BGPVPNRCExtensionTestCase(test_extensions_base.ExtensionTestCase): # This is a modified copy of # n.t.u.extensions.base.ExtensionTestCase._setUpExtension # until the corresponding behavior, which consists in allowing # that more than one extension is setup, is pushed to neutron def _setUpExtension(self, plugin, service_type, _unused__resource_attribute_map, extension_class, *args, **kwargs): self._setUpExtensions(plugin, service_type, [extension_class], *args, **kwargs) def _setUpExtensions(self, plugin, service_type, extension_classes, resource_prefix, plural_mappings=None, translate_resource_name=False, allow_pagination=False, allow_sorting=False, supported_extension_aliases=None, use_quota=False, ): self._resource_prefix = resource_prefix self._plural_mappings = plural_mappings or {} self._translate_resource_name = translate_resource_name # Ensure existing ExtensionManager is not used extensions.PluginAwareExtensionManager._instance = None self.useFixture(fixture.APIDefinitionFixture()) # Create the default configurations self.config_parse() core_plugin = CORE_PLUGIN if service_type else plugin self.setup_coreplugin(core_plugin, load_plugins=False) if service_type: cfg.CONF.set_override('service_plugins', [plugin]) self._plugin_patcher = mock.patch(plugin, autospec=True) self.plugin = self._plugin_patcher.start() instance = self.plugin.return_value if service_type: instance.get_plugin_type.return_value = service_type manager.init() if supported_extension_aliases is not None: instance.supported_extension_aliases = supported_extension_aliases if allow_pagination: # instance.__native_pagination_support = True native_pagination_attr_name = ("_%s__native_pagination_support" % instance.__class__.__name__) setattr(instance, native_pagination_attr_name, True) if allow_sorting: # instance.__native_sorting_support = True native_sorting_attr_name = ("_%s__native_sorting_support" % instance.__class__.__name__) setattr(instance, native_sorting_attr_name, True) if use_quota: quota.QUOTAS._driver = None cfg.CONF.set_override('quota_driver', 'neutron.quota.ConfDriver', group='QUOTAS') setattr(instance, 'path_prefix', resource_prefix) #################################################################### ext_mgr = extensions.ExtensionManager('') for extension_class in extension_classes: ext = extension_class() ext_mgr.add_extension(ext) # NOTE(tmorin): maybe use attributes directly from neutron_lib ? ext_mgr.extend_resources("2.0", attributes.RESOURCE_ATTRIBUTE_MAP) #################################################################### self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr) self.api = webtest.TestApp(self.ext_mdw) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/extensions/__init__.py0000666000175100017510000000000013245511235027411 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn_vni.py0000666000175100017510000001047213245511235031077 0ustar zuulzuul00000000000000# # Copyright 2017 Ericsson India Global Services 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 copy import mock from oslo_utils import uuidutils from neutron.tests.unit.api.v2 import test_base from neutron.tests.unit.extensions import base as test_extensions_base from neutron_lib.api.definitions import bgpvpn as bgpvpn_def from neutron_lib.api.definitions import bgpvpn_vni as bgpvpn_vni_def from webob import exc from networking_bgpvpn.neutron.extensions import bgpvpn _uuid = uuidutils.generate_uuid _get_path = test_base._get_path BGPVPN_PREFIX = 'bgpvpn' BGPVPN_URI = BGPVPN_PREFIX + '/' + 'bgpvpns' BGPVPN_PLUGIN_BASE_NAME = ( bgpvpn.BGPVPNPluginBase.__module__ + '.' + bgpvpn.BGPVPNPluginBase.__name__) class BgpvpnVniTestExtensionManager(object): def get_resources(self): bgpvpn_def.RESOURCE_ATTRIBUTE_MAP[bgpvpn_def.COLLECTION_NAME].update( bgpvpn_vni_def.RESOURCE_ATTRIBUTE_MAP[bgpvpn_def.COLLECTION_NAME]) return bgpvpn.Bgpvpn.get_resources() def get_actions(self): return [] def get_request_extensions(self): return [] class BgpvpnVniExtensionTestCase(test_extensions_base.ExtensionTestCase): fmt = 'json' def setUp(self): super(BgpvpnVniExtensionTestCase, self).setUp() plural_mappings = {'bgpvpn': 'bgpvpns'} self._setUpExtension( BGPVPN_PLUGIN_BASE_NAME, bgpvpn_def.LABEL, None, BgpvpnVniTestExtensionManager(), BGPVPN_PREFIX, plural_mappings=plural_mappings, translate_resource_name=True) self.instance = self.plugin.return_value def test_bgpvpn_create(self): bgpvpn_id = _uuid() data = { 'bgpvpn': {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'vni': 1000, 'tenant_id': _uuid()} } expected_ret_val = copy.copy(data['bgpvpn']) expected_ret_val['import_targets'] = [] expected_ret_val['export_targets'] = [] expected_ret_val['route_distinguishers'] = [] expected_ret_val['vni'] = 1000 expected_call_args = copy.copy(expected_ret_val) expected_ret_val.update({'id': bgpvpn_id}) self.instance.create_bgpvpn.return_value = expected_ret_val res = self.api.post(_get_path(BGPVPN_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt) self.assertTrue(self.instance.create_bgpvpn.called) self.assertDictSupersetOf( expected_call_args, self.instance.create_bgpvpn.call_args[1]['bgpvpn']['bgpvpn']) self.assertEqual(res.status_int, exc.HTTPCreated.code) res = self.deserialize(res) self.assertIn('bgpvpn', res) self.assertDictSupersetOf(expected_ret_val, res['bgpvpn']) def test_bgpvpn_get(self): bgpvpn_id = _uuid() return_value = {'name': 'bgpvpn1', 'type': 'l3', 'route_targets': ['1234:56'], 'tenant_id': _uuid(), 'vni': 1000, 'id': bgpvpn_id} self.instance.get_bgpvpn.return_value = return_value res = self.api.get(_get_path(BGPVPN_URI, id=bgpvpn_id, fmt=self.fmt)) self.instance.get_bgpvpn.assert_called_with( mock.ANY, bgpvpn_id, fields=mock.ANY ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('bgpvpn', res) self.assertEqual(res['bgpvpn'], return_value) def test_bgpvpn_delete(self): self._test_entity_delete('bgpvpn') networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn_routes_control.py0000666000175100017510000002610113245511235033360 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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 copy import mock from oslo_utils import uuidutils from neutron.extensions import l3 from neutron.tests.unit.api.v2 import test_base from neutron_lib.api.definitions import bgpvpn as bgpvpn_api_def from neutron_lib.api.definitions import bgpvpn_routes_control as rc_api_def from webob import exc from networking_bgpvpn.neutron.extensions import bgpvpn from networking_bgpvpn.neutron.extensions \ import bgpvpn_routes_control as bgpvpn_rc from networking_bgpvpn.tests.unit.extensions import test_bgpvpn_rc_base _uuid = uuidutils.generate_uuid _get_path = test_base._get_path BGPVPN_PREFIX = 'bgpvpn' BGPVPN_URI = BGPVPN_PREFIX + '/' + 'bgpvpns' class TestPlugin(bgpvpn.BGPVPNPluginBase, bgpvpn_rc.BGPVPNRoutesControlPluginBase): supported_exsupported_extension_aliases = [bgpvpn_api_def.ALIAS, rc_api_def.ALIAS] TEST_PLUGIN_CLASS = '%s.%s' % (TestPlugin.__module__, TestPlugin.__name__) class BgpvpnRoutesControlExtensionTestCase( test_bgpvpn_rc_base.BGPVPNRCExtensionTestCase): def setUp(self): super(BgpvpnRoutesControlExtensionTestCase, self).setUp() self._setUpExtensions( TEST_PLUGIN_CLASS, bgpvpn_api_def.LABEL, [l3.L3, bgpvpn.Bgpvpn, bgpvpn_rc.Bgpvpn_routes_control], BGPVPN_PREFIX, translate_resource_name=True) self.instance = self.plugin.return_value self.bgpvpn_id = _uuid() self.net_id = _uuid() self.router_id = _uuid() self.net_assoc_id = _uuid() self.router_assoc_id = _uuid() self.port_id = _uuid() self.port_assoc_id = _uuid() self.NET_ASSOC_URI = BGPVPN_URI + '/' + self.bgpvpn_id + \ '/network_associations' self.ROUTER_ASSOC_URI = BGPVPN_URI + '/' + self.bgpvpn_id + \ '/router_associations' self.PORT_ASSOC_URI = BGPVPN_URI + '/' + self.bgpvpn_id + \ '/port_associations' def _invalid_data_for_creation(self, target): return [None, {}, {target: None}, {target: {}} ] def test_router_association_update(self): data = { 'router_association': { 'router_id': self.router_id, 'project_id': _uuid() } } self.api.post(_get_path(self.ROUTER_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) update_data = {'router_association': { 'advertise_extra_routes': False, }} return_value = { 'project_id': _uuid(), 'advertise_extra_routes': False, } self.instance.update_bgpvpn_router_association.return_value = ( return_value) res = self.api.put(_get_path(self.ROUTER_ASSOC_URI, id=self.router_assoc_id, fmt=self.fmt), self.serialize(update_data), content_type='application/%s' % self.fmt) self.instance.update_bgpvpn_router_association.assert_called_with( mock.ANY, self.router_assoc_id, bgpvpn_id=self.bgpvpn_id, router_association=update_data ) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(res) self.assertIn('router_association', res) self.assertEqual(return_value, res['router_association']) def _invalid_data_for_port_assoc(self): return [ ({'advertise_fixed_ips': 'foo'}, "cannot be converted to boolean"), ({'routes': 'bla'}, "is not a list"), ({'routes': [{ 'type': 'flumox'}]}, "No valid key specs"), ({'routes': [{ 'type': 'prefix', 'something_else_than_prefix': 'foo' }]}, "No valid key specs"), ({'routes': [{ 'type': 'prefix', 'prefix': '1.1.1.352' }]}, "No valid key specs"), ({'routes': [{ 'type': 'prefix', 'something_else_than_bgpvpn_id': 'foo' }]}, "No valid key specs"), ({'routes': [{ 'type': 'prefix', 'prefix': '12.1.2.3', 'local_pref': -1, }]}, "No valid key specs"), ({'routes': [{ 'type': 'prefix', 'prefix': '12.1.2.3/20', 'local_pref': 2 ** 32, }]}, "No valid key specs") ] def test_port_association_create(self): data = { 'port_association': { 'port_id': self.port_id, 'tenant_id': _uuid() } } return_value = copy.copy(data['port_association']) return_value.update({'id': self.port_assoc_id}) self.instance.create_bgpvpn_port_association.return_value = \ return_value res = self.api.post(_get_path(self.PORT_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertTrue(self.instance.create_bgpvpn_port_association.called) self.assertEqual(self.bgpvpn_id, self.instance.create_bgpvpn_port_association. call_args[1]['bgpvpn_id']) self.assertDictSupersetOf( data['port_association'], self.instance.create_bgpvpn_port_association. call_args[1]['port_association']['port_association']) self.assertIn('port_association', res) res = self.deserialize(res) self.assertDictSupersetOf(return_value, res['port_association']) def _test_port_association_create_with_invalid_data(self, port_assoc, msg): res = self.api.post(_get_path(self.PORT_ASSOC_URI, fmt=self.fmt), self.serialize(port_assoc), content_type='application/%s' % self.fmt, expect_errors=True) self.assertEqual(res.status_int, exc.HTTPBadRequest.code) self.assertFalse( self.instance.create_bgpvpn_port_association.called) self.assertIn(msg, str(res.body)) def test_port_association_create_with_invalid_assoc(self): for data in self._invalid_data_for_creation('port_association'): res = self.api.post(_get_path(self.PORT_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) self.assertFalse( self.instance.create_bgpvpn_port_association.called) self.assertEqual(res.status_int, exc.HTTPBadRequest.code) def test_port_association_create_with_invalid_content(self): for port_assoc_attrs, msg in self._invalid_data_for_port_assoc(): data = {'port_association': { 'port_id': self.port_id, 'project_id': _uuid() } } data['port_association'].update(port_assoc_attrs) self._test_port_association_create_with_invalid_data(data, msg) def test_port_association_get(self): return_value = {'id': self.port_assoc_id, 'port_id': self.port_id} self.instance.get_bgpvpn_port_association.return_value = \ return_value res = self.api.get(_get_path(self.PORT_ASSOC_URI, id=self.port_assoc_id, fmt=self.fmt)) self.instance.get_bgpvpn_port_association.assert_called_with( mock.ANY, self.port_assoc_id, self.bgpvpn_id, fields=mock.ANY ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('port_association', res) self.assertEqual(return_value, res['port_association']) def test_port_association_update(self): data = { 'port_association': { 'port_id': self.port_id, 'project_id': _uuid() } } self.api.post(_get_path(self.PORT_ASSOC_URI, fmt=self.fmt), self.serialize(data), content_type='application/%s' % self.fmt, expect_errors=True) update_data = {'port_association': { 'advertise_fixed_ips': False, 'routes': [ {'type': 'prefix', 'prefix': '1.2.3.0/24', 'local_pref': 42}, {'type': 'bgpvpn', 'bgpvpn_id': _uuid()}, ] }} return_value = { 'port_id': self.port_id, 'project_id': _uuid(), 'advertise_fixed_ips': False, 'routes': [ {'type': 'prefix', 'prefix': '1.2.3.0/24', 'local_pref': 42}, {'type': 'bgpvpn', 'prefix': '1.2.3.0/24'}, ] } self.instance.update_bgpvpn_port_association.return_value = ( return_value) res = self.api.put(_get_path(self.PORT_ASSOC_URI, id=self.port_assoc_id, fmt=self.fmt), self.serialize(update_data), content_type='application/%s' % self.fmt) self.instance.update_bgpvpn_port_association.assert_called_with( mock.ANY, self.port_assoc_id, bgpvpn_id=self.bgpvpn_id, port_association=update_data ) self.assertEqual(res.status_int, exc.HTTPOk.code) res = self.deserialize(res) self.assertIn('port_association', res) self.assertEqual(res['port_association'], return_value) def test_port_association_delete(self): res = self.api.delete(_get_path(self.PORT_ASSOC_URI, id=self.port_assoc_id, fmt=self.fmt)) self.instance.delete_bgpvpn_port_association.assert_called_with( mock.ANY, self.port_assoc_id, self.bgpvpn_id) self.assertEqual(res.status_int, exc.HTTPNoContent.code) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/client/0000775000175100017510000000000013245511747024377 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/client/__init__.py0000666000175100017510000000000013245511235026470 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/unit/client/test_client.py0000666000175100017510000000477613245511235027276 0ustar zuulzuul00000000000000# Copyright (c) 2015 Orange. # 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.unit.extensions import base as test_extensions_base from neutronclient.v2_0 import client BGPVPN_ID = "uuid-bgpvpn-foo" NET_ASSOC_ID = "uuid-netassoc-bar" ASSOCS_PATH = "/bgpvpn/bgpvpns/%s/network_associations" % BGPVPN_ID ASSOC_PATH = "/bgpvpn/bgpvpns/%s/network_associations/%%s" % BGPVPN_ID class BgpvpnClientTestCase(test_extensions_base.ExtensionTestCase): def setUp(self): super(BgpvpnClientTestCase, self).setUp() self.client = client.Client() self.client.list_ext = mock.Mock() self.client.create_ext = mock.Mock() self.client.show_ext = mock.Mock() self.client.update_ext = mock.Mock() self.client.delete_ext = mock.Mock() def test_api_url_list(self): self.client.list_network_associations(BGPVPN_ID) self.client.list_ext.assert_called_once_with(mock.ANY, ASSOCS_PATH, mock.ANY) def test_api_url_create(self): self.client.create_network_association(BGPVPN_ID, {}) self.client.create_ext.assert_called_once_with(ASSOCS_PATH, mock.ANY) def test_api_url_show(self): self.client.show_network_association(NET_ASSOC_ID, BGPVPN_ID) self.client.show_ext.assert_called_once_with(ASSOC_PATH, NET_ASSOC_ID) def test_api_url_update(self): self.client.update_network_association(NET_ASSOC_ID, BGPVPN_ID, {}) self.client.update_ext.assert_called_once_with(ASSOC_PATH, NET_ASSOC_ID, mock.ANY) def test_api_url_delete(self): self.client.delete_network_association(NET_ASSOC_ID, BGPVPN_ID) self.client.delete_ext.assert_called_once_with(ASSOC_PATH, NET_ASSOC_ID) networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/0000775000175100017510000000000013245511747024304 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/db/0000775000175100017510000000000013245511747024671 5ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/db/test_migrations.py0000666000175100017510000000510013245511235030444 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_config import cfg from neutron.db.migration.alembic_migrations import external from neutron.db.migration import cli as migration from neutron.tests.functional.db import test_migrations from neutron.tests.unit import testlib_api from networking_bgpvpn.neutron.db import head # Tables from other repos that we depend on but do not manage. IGNORED_TABLES_MATCH = ( 'ml2_route_target_allocations', '_bagpipe_', 'odl_', 'opendaylight' ) # EXTERNAL_TABLES should contain all names of tables that are not related to # current repo. EXTERNAL_TABLES = set(external.TABLES) VERSION_TABLE = 'alembic_version_bgpvpn' class _TestModelsMigrationsBGPVPN(test_migrations._TestModelsMigrations): def db_sync(self, engine): cfg.CONF.set_override('connection', engine.url, group='database') for conf in migration.get_alembic_configs(): self.alembic_config = conf self.alembic_config.neutron_config = cfg.CONF migration.do_alembic_command(conf, 'upgrade', 'heads') def get_metadata(self): return head.get_metadata() def include_object(self, object_, name, type_, reflected, compare_to): if type_ == 'table' and (name.startswith('alembic') or name == VERSION_TABLE or name in EXTERNAL_TABLES or any([match in name for match in IGNORED_TABLES_MATCH])): return False if type_ == 'index' and reflected and name.startswith("idx_autoinc_"): return False return True class TestModelsMigrationsMysql(testlib_api.MySQLTestCaseMixin, _TestModelsMigrationsBGPVPN, testlib_api.SqlTestCaseLight): pass class TestModelsMigrationsPostgresql(testlib_api.PostgreSQLTestCaseMixin, _TestModelsMigrationsBGPVPN, testlib_api.SqlTestCaseLight): pass networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/db/__init__.py0000666000175100017510000000000013245511235026762 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/test_placeholder.py0000666000175100017510000000016213245511235030170 0ustar zuulzuul00000000000000from neutron.tests import base class PlaceholderTest(base.BaseTestCase): def test_noop(self): pass networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/__init__.py0000666000175100017510000000000013245511235026375 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/networking_bgpvpn/tests/functional/requirements.txt0000666000175100017510000000040213245511235027556 0ustar zuulzuul00000000000000# Additional requirements for functional tests # 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. networking-bgpvpn-8.0.0/networking_bgpvpn/tests/__init__.py0000666000175100017510000000000013245511235024233 0ustar zuulzuul00000000000000networking-bgpvpn-8.0.0/CONTRIBUTING.rst0000666000175100017510000000103113245511235017663 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: http://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/bgpvpn networking-bgpvpn-8.0.0/.stestr.conf0000666000175100017510000000011713245511235017477 0ustar zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./networking_bgpvpn/tests/unit} top_dir=./ networking-bgpvpn-8.0.0/PKG-INFO0000664000175100017510000000463213245511747016337 0ustar zuulzuul00000000000000Metadata-Version: 1.1 Name: networking-bgpvpn Version: 8.0.0 Summary: API and Framework to interconnect bgpvpn to neutron networks Home-page: https://docs.openstack.org/networking-bgpvpn/latest/ Author: OpenStack Author-email: openstack-dev@lists.openstack.org License: UNKNOWN Description-Content-Type: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/networking-bgpvpn.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on =============================================== BGP-MPLS VPN Extension for OpenStack Networking =============================================== This project provides an API and Framework to interconnect BGP/MPLS VPNs to Openstack Neutron networks, routers and ports. The Border Gateway Protocol and Multi-Protocol Label Switching are widely used Wide Area Networking technologies. The primary purpose of this project is to allow attachment of Neutron networks and/or routers to VPNs built in carrier provided WANs using these standard protocols. An additional purpose of this project is to enable the use of these technologies within the Neutron networking environment. A vendor-neutral API and data model are provided such that multiple backends may be "plugged in" while offering the same tenant facing API. A reference implementation based on an Open Source BGP implementation is also provided. * Free software: Apache license * Source: http://git.openstack.org/cgit/openstack/networking-bgpvpn * Bugs: http://bugs.launchpad.net/bgpvpn * Doc: https://docs.openstack.org/networking-bgpvpn/latest/ 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 :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.5 networking-bgpvpn-8.0.0/ChangeLog0000664000175100017510000004700713245511745017015 0ustar zuulzuul00000000000000CHANGES ======= 8.0.0 ----- * switch to use new DB facade * ‘local\_pref’ can be updated in 'test\_bgpvpn\_create\_update\_delete()' * enable tempest RT update test * routes-control: DB, adjust lazy loading * requirements.txt hints for deps managed in tools/tox\_install.sh * tempest test improvements * Zuul: Remove project name * Updated from global requirements * functional test fix: ignore more tables in DB consistency check * Local\_pref attr tempest test for port association * bagpipe: advertise support for VNI extension 8.0.0.0b3 --------- * tempest: enable test\_bgpvpn\_port\_association\_create\_and\_update * Updated from global requirements * doc: formatting fix for OSC doc link * db: minor, add missing DB migration script message * bagpipe v2 driver * db: refresh port association db object after route update * Deprecates old OpenContrail driver * Fix small typo in docs configuration file * routes-control: add 'local\_pref' attribute to BGPVPN resource * Basic tempest tests for port associations * Utility functions for port association tests in tempest * Updated from global requirements * Updated from global requirements * routes-control: add advertise\_extra\_routes to router\_association * Updated from global requirements * bagpipe: remove use of BGPVPNAssociations * [bgpvpn\_dashboard] Fix bug when a network or router name doesn't exist * bagpipe driver: add support for Port Associations * bagpipe driver doc update * [bgpvpn\_dashboard] Minor typo fix * Upgrade hacking specs * Fix minor problem in bgpvpn\_dashboard unit test * Add missing tempest tests for listing and showing objects * Add vni attribute to bgpvpn resource * change how drivers indicate support for an extension * make get\_extended\_resources class methods * bagpipe driver: use OVO-based push/pull RPCs * Imported Translations from Zanata * Updated from global requirements * Adding idempotent IDs to tempest tests * Imported Translations from Zanata 8.0.0.0b2 --------- * Improve message information for translation * Imported Translations from Zanata * Imported Translations from Zanata * Updated from global requirements * Imported Translations from Zanata * devstack: fix linuxbridge configuration * Use SQL BigInteger type to store BGP LOCAL\_PREF * zuul: run tripleo scenario004 like before * tempest: update to follow code deprecation * Remove policy check * bagpipe: fix BGPVPN update/delete for router association * routes-control: fix driver class for non-DB drivers * Remove setting of version/release from releasenotes * Updated from global requirements * Check if bgpvpn enabled in tempest test * Various tempest tests of L3 BGPVPN update * Updated from global requirements * routes-control: port associations (API ext, DB, driver API) * Tempest tests with delete operations * Tempest tests to check L3 BGPVPN RTs update * Updated from global requirements * Tempest utility functions modified * dashboard: impossible to add a bgpvpn with a empty route target * Updated from global requirements * Filter duplicated RTs in compiled list * Test that an empty RT is not accepted * Update doc to use openstack CLI instead neutron CLI 8.0.0.0b1 --------- * dashboard: edit variable containing Regex RT * dashboard: Add unit tests for bgpvpn\_dashboard * Replace the usage of some aliases in tempest * functional tests: ignore all ODL tables * Replace the usage of some aliases in tempest * devstack fixes for linuxbridge * bagpipe driver: enable l2vpn * Update reno for stable/mitaka * Switch DB and driver precommit methods for delete and update * dashboard: introduce usage of the policy file * dashboard: clean code in forms.py * Updated from global requirements * Shrink Tempest scenario manager copy * Updated from global requirements * Two negative tempest tests added * Modified utility functions for negative tests * dashboard: fix bug about route target validation * Updated from global requirements * Fix post gate hook to accommodate for new os-testr * Two new tempest test variants are added * Bug fix in tempest tests * Drop MANIFEST.in - it's not needed by pbr * Updated from global requirements * Updated from global requirements * Update reno for stable/pike * Tempest test base modified and new variants added 7.0.0.0rc1 ---------- * Updated from global requirements * Remove WebTest from test requirements * Add auto-generated config reference * Automatically generate configuration files 7.0.0.0b3 --------- * Updated from global requirements * Replace deprecated test.attr with decorators.attr * Replace deprecated test.attr with decorators.attr * Translation support * Update the documentation link for doc migration * Updated from global requirements * dashboard: refactor views * Remove "=None" in call to \_make\_net\_assoc\_dict * Add driver compatibility matrix to documentation * Updated from global requirements * dashboard: change admin panel * dashboard: fix call of method patterns * Using neutron-lib hacking rules * doc: rendering cleanup * Rearrange existing documentation to fit the new standard layout * Switch from oslosphinx to openstackdocstheme * Turn on warning-is-error in doc build * misc cleanups * bgpvpn-routes-control: policy.json * devstack: declare n-api-meta * policy.json: remove unimplemented attributes * dashboard: change clean method in create and update * dashboard: fix bug about the create BGPVPN form * Speed up tox\_install.sh * use service type constants from neutron\_lib plugins * Updated from global requirements * dashboard: allow bgpvpns with the same name * dashboard: fix RT validation * Add unit tests for bgpvpn\_dashboard * Replace the usage of 'manager' with 'os\_primary' * Updated from global requirements * Updated from global requirements * doc, bagpipe/ovs driver update * update doc on installation and versions * network\_association\_delete function log errors * Updated from global requirements 7.0.0.0b2 --------- * use networking-odl from pypi instead of git master * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * dashboard: fix constant import for RT/RD validation * Updated from global requirements * Updated from global requirements * Updated from global requirements * devstack: source neutron l2 agent script only if q-agt enabled * Remove windows-style line breaks * consume neutron-lib callbacks * use i18n.\_ * Register query hooks at BGPVPNPluginDb object creation * Add net-bgpvpn.conf to config file read by neutron * Add constants for bgpvpn\_tests * Updated from global requirements * Updated from global requirements * Move API definition out of n8g-bgpvpn into neutron-lib 7.0.0.0b1 --------- * Updated from global requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * remove bagpipe\_bgpvpn agent extension * Stop using config backed quota engine in unit tests * bagpipe driver: cleanup, remove OVSInterceptBridge * Point API entry to neutron-lib API Reference * devstack job config cleanups * consume ServicePluginBase from neutron-lib * Updated from global requirements * Update bagpipe driver documentation * Indicating the location tests directory in oslo\_debug\_helper * Switch to use stable data\_utils * Updated from global requirements * 'bgpvpn' entry point for neutron.conf:service\_plugins= * Switch to use stable data\_utils * Updated from global requirements * tempest: Switch to local copy of tempset/scenario/manager.py * leave branch unspecified for "current" series * Updated from global requirements * Updated from global requirements * bagpipe: follow move of db/models * Update reno for stable/ocata * Use neutron-lib's context module * Introduce precommit hooks for delete\_bgpvpn\_xxx 6.0.0 ----- * Log a deprecated warning for ODL v1 driver * Revert "Add OpenStack client BGP VPN extension" * Enable multiple RDs of a BGPVPN to be passed to OpenDaylight * doc: improve explanation on the location of config files * Advertise support for python 3 and drop py34 jobs * Typo fix: datas => data * Prepare for using standard python tests * Filtering BGP VPN list with resource associations * devstack job config: add placement-api service * Improve pip installation documentation * Remove doc modindex ref * Use neutron-lib portbindings api-def * Updated from global requirements * Updated from global requirements * Add router association dict formatter method * Removes unnecessary utf-8 encoding * Fix reno release naming and dvsm functional job * LOG marker mismatch in plugin.py * Follow ODL's master * Revert "Temporary workaround to our gate issues" * Adds Tempest scenario test for networking-bgpvpn * devstack: configure tempest at test-config stage * Use ExtensionDescriptor from neutron-lib * Use DB field sizes instead of \_MAX\_LEN constants * Show team and repo badges on README * Use model\_base in neutron\_lib * Switch to using plugins directory in lieu of neutron manager * Replace six.iteritems() with .items() * Use uuidutils instead of uuid.uuid4() * Replace LOG.warn with LOG.warning * Updated from global requirements * Updated from global requirements * Install networking-bagpipe test dependency via tox\_install/zuul-cloner * Use temporary directory for neutron and horizon install * Temporary workaround to our gate issues * OpenContrail : fix exception class usage issues * Remove white space between print () in bgpvpn-sample01.py * Add OpenStack client BGP VPN extension * Install the networking\_bgpvpn\_heat package * Fix tox cover target * Remove last vestiges of oslo-incubator * migration tests as functional tests * Remove custom OVS compilation trigger for Openstack CI * Don't include openstack/common in flake8 exclude list * bagpipe: rely on ROUTER\_INTERFACE registry callbacks * python3: bagpipe driver fix for bridge cookies * devstack/bagpipe: pin OVS to branch-2.5 * Changed the home-page link * Update reno for stable/mitaka * Modify bgpvpn relations with association tables to select * Update reno for stable/newton * Support infrastructure for functional tests * Fix error when the tenant of a bgpvpn resource doesn't exist 5.0.0 ----- * undo some of the incorrect changes for prepping Newton * prepare Newton release * Stop adding ServiceAvailable group option * Pin ODL's dependency to a working commit * bagpipe: compatibility with Neutron routers * opencontrail: not check tenant existence on update * import validate\_regex from neutron\_lib * Enable release notes translation * devstack: fix to load bagpipe l2 agent extension * bagpipe: rely on Port AFTER\_DELETE callbacks * bagpipe: port+orig\_port are in Port AFTER\_UPDATE callbacks * Prevent mixing bgpvpn associations * Fix a typo in documentation * Remove python 3 from setup.cfg classifiers * Import DB model\_base from neutron-lib * Use os-testr instead of testr * TrivialFix: Remove logging import unused * devstack, bagpipe driver: properly set the l2 agent extension * Horizon plugin patch to let user handle BGPVPN resources * Add doc for devstack configuration * Use horizon UT framework * Use more permissive UTs * Enable L2 BGPVPN to be passed to OpenDaylight * Add tempest tests for router association * Add more tempest tests for read permissions * Raise NotImplementedError instead of NotImplemented * Use constrained pip install for all jobs * Remove windows-style line breaks * Import \_ explicitly from .\_i18n * Add error management regarding malformed UUID * Add a tempest test on read permission with bgpvpn\_list * Added the negative cases * Add tempest tests on route-target update * import api validators/converters from neutron\_lib * enable tempest tests for bgpvpn * unit test fix: specify the tenand\_id at Port/Net/Subnet creation * Bad parameter name in disassociate\_network\_from\_bgpvpn * Add a tenant ID check to create a bgpvpn resource * Remove temporary local HasProject * Enable DeprecationWarning in test environments * Add tempest test associate\_disassociate\_network * Delete execute permission of two files * Add Python 3.5 classifier and venv * Updated from global requirements * Updated from global requirements * Remove discover from test-requirements * Add test delete\_bgpvpn\_as\_non\_admin\_fail * Rename DB columns: tenant -> project * Bring models in sync with migrations, add test * Fix the permission of file -rwxr-xr-x * Update API usage with Python and a sample code * Remove useless/broken call in a bagpipe driver test * Remove unused LOG to keep code clean * Improve bagpipe unittest involving OVS bridges * Fix tox unit test issue * Initialize the routers key in make\_bgpvpn\_dict * minor doc layout improvement * Horizon plugin to let the admin handle BGPVPN * Fix RD regex to match RFC 4364, chapter 4.2 * Move from neutron.i18n to oslo.i18n * Update OpenContrail driver documentation * README cleanup * Update bagpipe driver documentation * Make test jobs constrained * Import neutron exceptions from neutron\_lib * bagpipe driver: add a unit test for agent extension * Import constants from neutron\_lib * bagpipe: improve unit test * bagpipe driver: enable a previously disabled unit test * Fix typos in bgpvpn installation manual * bagpipe: really use the extension-specific cookie * bagpipe: update unit tests to follow a Neutron ML2 change * Typo in OpenContrail driver documentation manual * Add info on Nuage Networks driver * Improve installation documentation 4.0.0 ----- * bagpipe: ignore all probe ports * devstack: fix OVS compilation hook * Updated from global requirements * bagpipe: do not ignore probe ports * Add release notes for mitaka * fix release notes build * ODL: Add precommit to create/update\_bgpvpn * Add support for reno release-notes manager * devstack job: enable bagpipe-bgp in bagpipe jobs * Add limitation chapter to bagpipe doc * bagpipe: skip network:\* ports and external networks ports * Add dummy gate\_hook.sh * bagpipe driver: no RPC for updates not changing port status * Add precommit checks to bagpipe driver * devstack: add pre|post\_test\_hook.sh files * Add precommit hooks for create\_bgpvpn\_net/router\_assoc * Add precommit to the driver create/update\_bgpvpn API * Use bagpipe l2 agent extension when bagpipe is activated * Update and improve bagpipe driver documentation * correcting url for nuage website * [Tempest] test\_create\_bgpvpn\_as\_non\_admin\_fail * Uplift to latest Tempest * Adding missings () after a method call * OVS Agent extension for bagpipe driver * Add rcfiles for gate jobs * Fix client test to follow python-neutronclient change * Heat: allow names instead of ids in templates * Heat: improve documentation * Add BGPVPN-ROUTER-ASSOCIATION to heat plugin * Initialize Heat plugin * Devstack : configure tempest file during extra hook * remove neutron-client@liberty dependency * Fixing pylint upgrade issues * Fix rendering issues on block diagrams * bagpipe: agent, update RPC setup code * Follow Neutron master * Add support for router association in ODL driver * bagpipe: only select gw for IPv4 subnets * py26/py33 are no longer supported by Infra's CI * remove python 2.6 trove classifier 3.0.0 ----- * Add a type restriction to bgpvpns when creating a router association * Fix RT/RD validation * Update the spec : remove RTs consolidation part * Add unit tests for neutronclient extension * Add help to Neutron BGVPN CLI commands * bagpipe: always pass network info to the agent * Implementation of router bgpvpn associations * Raise an exception for identical associations * Remove partial implementation of auto\_aggregate * bagpipe: work on notification without port info * Zuul tox needs to use the right neutron branch * Enable bgpvpn in tempest * Init tempest plugin * pylint fixes + pylint downgrade * bagpipe driver: catch exceptions on Neutron notifs * Added documentation for ODL Driver * WiP: Porting bgpvpn odldriver * driver documentation layout fix * bagpipe: port delete action on BEFORE\_DELETE event * Add OpenContrail driver * Remove a spurious tab in setup.cfg * Fix typo in API error message * Add tenant-id to subresources * Remove hardcoded utf8 coding for bgpvpns table * Fix driver control through devstack * Checks consistency of net association vs BGPVPN id * Fix bogus pip install URI in tox\_install.sh * Other adjustments following bagpipe rename * rename bagpipe-l2 in test-requirements * Client : adding the tenant-id if specified for a net association creation * have tox use neutron stable/liberty * Fix oslo dependencies * Do not enforce non-empty route-target lists * bagpipe-l2 now in openstack * Client support for associations as sub-resource * Treat associations as subresources * Populate doc directory * Updated from global requirements * Updated from global requirements * Updated from global requirements * Fix devstack plugin.sh * add pylint in tox pep8 task * pylint fixes * Updated from global requirements * Let devstack configure the service provider * Alembic migration update/cleanup * Updated from global requirements * update requirements * bagpipe driver: sync extended OVS agent * Neutronclient: one command for all associations * Tiny formatting and grammar fix * Add introductory documentation for networking-bgpvpn * bagpipe driver: missing return to ignore DHCP ports * Change ignore-errors to ignore\_errors * Set correct default values for some attributes * Updated from global requirements * Adding network association management : bagpipe driver * Fix README : rename networking-bagpipe-l2 links * Fix resource map to enforce policy * neutronclient: unbreak create/update * Fix README : use openstack git instead of github * devstack: add service plugin class earlier * Log a warning if multiple drivers configured * devstack: use helper to add a service plugin * Refer to the new specs * Adapts neutronclient to the new association API * Adding network association management : API & DB layer * bagpipe: follow RPC renaming * Bagpipe driver: use Neutron registry not an ML2 MD * Fixes JSON policy, tenant\_id control and nits * Fix a typo in README.rst * Add route\_distinguishers field to the DB scheme * Fix name of service provider config file * Read networking\_bgpvpn.conf for service providers * Devstack plugin: create neutron policy.d * Complete change 218359 * oslo.config is now oslo\_config * API/DB should not duplicate the policy framework * Updated from global requirements * Add tests for the service driver interface * Updated from global requirements * Updated from global requirements * Add policy.json * Fix one typo on networking-bgpvpn documentation * README improvements * Updated from global requirements * typo correction from BGPVPNDriverBD to BGPVPNDriverDB * Remove BGPVPN plugin dependency on database * Update BaGPipe OVS agent * Neutron constant COMMON\_PREFIXES does not exist anymore * Avoid cloning neutron on test jobs * README/devstack: fix spurious space in IPVPN driver specification * Follow neutron service plugin definition change * Complete the removal of bgpvpn module * Improved README-bagpipe for devstack use * Support route\_distinguishers operation * update README-bagpipe.rst for bagpipe-bgp devstack plugin * Remove bgpvpn module when useless * Move from n.o.commons.uuidutils to osloutils.uuid\_utils * Network\_id should not be a foreign key * First database unit tests * devstack: do not run update-db on a compute node * links to stackforge now link to openstack * Update .gitreview file for project rename * Adding initial unit tests * add bagpipe driver and agent * Adding the initial spec implemented by the bgpvpn framework * add devstack plugin * use oslo\_log.log instead of n.openstack.common.log * BGVPNDriver: some methods need not be abstract * use neutron.openstack.common.log rather than oslo\_log * fix bug on BGPVPN connection update * Update README * Initial proposal with db layer, API extension and client extension * Initial Cookiecutter Commit * Added .gitreview networking-bgpvpn-8.0.0/test-requirements.txt0000666000175100017510000000136613245511235021476 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!=0.13.0,<0.14,>=0.12.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 django-nose>=1.4.4 # BSD python-subunit>=1.0.0 # Apache-2.0/BSD sphinx!=1.6.6,>=1.6.2 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 oslosphinx>=4.7.0 # Apache-2.0 psycopg2>=2.6.2 # LGPL/ZPL PyMySQL>=0.7.6 # MIT License WebOb>=1.7.1 # MIT oslotest>=3.2.0 # Apache-2.0 os-testr>=1.0.0 # Apache-2.0 testresources>=2.0.0 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD testtools>=2.2.0 # MIT reno>=2.5.0 # Apache-2.0 pylint==1.4.5 # GPLv2 tempest>=17.1.0 # Apache-2.0 networking-bgpvpn-8.0.0/babel-django.cfg0000666000175100017510000000014713245511271020217 0ustar zuulzuul00000000000000[extractors] django = django_babel.extract:extract_django [python: **.py] [django: templates/**.html] networking-bgpvpn-8.0.0/requirements.txt0000666000175100017510000000134013245511235020511 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 Babel!=2.4.0,>=2.3.4 # BSD oslo.config>=5.1.0 # Apache-2.0 oslo.db>=4.27.0 # Apache-2.0 oslo.i18n>=3.15.3 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 sphinxcontrib-blockdiag>=1.5.4 # BSD sphinxcontrib-seqdiag>=0.8.4 # BSD neutron-lib>=1.13.0 # Apache-2.0 debtcollector>=1.2.0 # Apache-2.0 # dependencies managed in tools/tox_install.sh for master branch: # - openstack/neutron # - openstack/horizon # - openstack/networking-bagpipe # - openstack/networking-odl networking-bgpvpn-8.0.0/setup.py0000666000175100017510000000200613245511235016737 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. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) networking-bgpvpn-8.0.0/.testr.conf0000666000175100017510000000055513245511235017322 0ustar zuulzuul00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ ${OS_TEST_PATH:-./networking_bgpvpn/tests/unit} $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list networking-bgpvpn-8.0.0/.zuul.yaml0000666000175100017510000000125613245511235017174 0ustar zuulzuul00000000000000- project: check: jobs: # TripleO jobs that deploy networking-bgpvpn. # Note we don't use a project-template here, so it's easier # to disable voting on one specific job if things go wrong. # tripleo-ci-centos-7-scenario004-multinode-oooq will only # run on stable/pike while the -container will run in Queens # and beyond. # If you need any support to debug these jobs in case of # failures, please reach us on #tripleo IRC channel. - tripleo-ci-centos-7-scenario004-multinode-oooq: voting: false - tripleo-ci-centos-7-scenario004-multinode-oooq-container: voting: false