networking-bgpvpn-12.0.0/0000775000175000017500000000000013656750625015314 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/0000775000175000017500000000000013656750625016061 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/requirements.txt0000664000175000017500000000073113656750513021342 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. sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD openstackdocstheme>=1.20.0 # Apache-2.0 reno>=2.5.0 # Apache-2.0 sphinxcontrib-blockdiag>=1.5.4 # BSD sphinxcontrib-seqdiag>=0.8.4 # BSD networking-bgpvpn-12.0.0/doc/source/0000775000175000017500000000000013656750625017361 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/introduction.rst0000664000175000017500000000226013656750513022630 0ustar zuulzuul00000000000000BGP-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, and inherits the base protocol architecture from BGP/MPLS IP VPNs. This service plugin exposes an API to interconnect OpenStack Neutron ports, typically VMs, via the Networks and Routers they are connected to, with a IP VPN as defined by [RFC4364]_ (BGP/MPLS IP Virtual Private Networks) or with an E-VPN [RFC7432]_. .. rubric:: Introduction videos: The following videos are filmed presentations of talks given during the Barcelona OpenStack Summit (Oct' 2016). Although they do not cover the work done since, they can be a good introduction to the project: * https://www.youtube.com/watch?v=kGW5R8mtmRg * https://www.youtube.com/watch?v=LCDeR7MwTzE networking-bgpvpn-12.0.0/doc/source/_static/0000775000175000017500000000000013656750625021007 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/_static/.placeholder0000664000175000017500000000000013656750513023254 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/conf.py0000664000175000017500000000743413656750513020664 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', 'oslo_policy.sphinxext', 'oslo_policy.sphinxpolicygen', ] # 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. 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' # 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 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' # -- Options for LaTeX output ------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', 'doc-networking-bgpvpn.tex', u'Networking BGPVPN Documentation', u'OpenStack Foundation', 'manual'), ] latex_elements = { 'extraclassoptions': 'openany,oneside', } # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'networking-bgpvpn.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 ] # -- Options for oslo_policy.sphinxpolicygen --------------------------------- policy_generator_config_file = '../../etc/oslo-policy-generator/policy.conf' sample_policy_basename = '_static/networking-bgpvpn' networking-bgpvpn-12.0.0/doc/source/install/0000775000175000017500000000000013656750625021027 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/install/index.rst0000664000175000017500000000634413656750513022673 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://releases.openstack.org/constraints/upper/ocata 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_v2.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 https://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 https://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-12.0.0/doc/source/samples/0000775000175000017500000000000013656750625021025 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/samples/__init__.py0000664000175000017500000000000013656750513023120 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/samples/bgpvpn-sample01.py0000664000175000017500000001253213656750513024312 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 os import sys from keystoneauth1.identity import v3 from keystoneauth1 import session from neutronclient.v2_0 import client # 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['project_name'] = os.environ['OS_PROJECT_NAME'] d['project_domain_id'] = os.environ['OS_PROJECT_DOMAIN_ID'] d['user_domain_id'] = os.environ['OS_USER_DOMAIN_ID'] 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() # Authentication auth = v3.Password(**creds) sess = session.Session(auth=auth) # Neutron object # It dynamically loads the BGPVPN API neutron = client.Client(session=sess) 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_bgpvpn_network_assoc( 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_bgpvpn_network_assocs( 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_bgpvpn_network_assoc( 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-12.0.0/doc/source/user/0000775000175000017500000000000013656750625020337 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/user/heat.rst0000664000175000017500000001075613656750513022017 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-12.0.0/doc/source/user/horizon.rst0000664000175000017500000000436613656750513022566 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-12.0.0/doc/source/user/workflows.seqdiag0000664000175000017500000000423513656750513023733 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-12.0.0/doc/source/user/components-sdn.blockdiag0000664000175000017500000000255713656750513025154 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-12.0.0/doc/source/user/usage.rst0000664000175000017500000001722713656750513022202 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 python ``neutroclient`` library includes support for the BGPVPN API extensions since Ocata release. .. note:: For older releases, the dynamic extension of ``neutronclient`` provided in ``networking-bgpvpn`` is available. In that case, the methods to list, get, create, delete and update network associations and router associations are different from what is documented here: * different name: ``list_network_associations`` instead of `list_bgpvpn_network_assocs``, and same change for all the methods * order of parameters: BGPVPN UUID as first parameter, association UUID as second parameter These old methods are deprecated. 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_bgpvpn_network_assocs()", "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_bgpvpn_network_assocs( BGPVPN UUID, param1=val1, param2=val2,...) (Optional)", "List of dictionaries of NETWORK ASSOCIATION attributes, one of each related to a given BGPVPN" "create_bgpvpn_network_assoc()", "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_bgpvpn_network_assoc()", "Get all parameters for a given NETWORK ASSOCIATION.", "1. UUID of the BGPVPN resource 2. UUID of the NETWORK ASSOCIATION resource", "Dictionary of NETWORK ASSOCIATION parameters" "update_bgpvpn_network_assoc()", "Update the parameters of the NETWORK ASSOCIATION resource provided as input.", "1. UUID of the BGPVPN resource 2. UUID of the NETWORK ASSOCIATION resource, 3. Dictionary of NETWORK ASSOCIATION parameters", "Dictionary of NETWORK ASSOCIATION parameters" "delete_bgpvpn_network_assoc()", "Delete a given NETWORK ASSOCIATION resource of which the UUID is provided as input.", "1. UUID of the BGPVPN resource 2. UUID of the NETWORK ASSOCIATION resource", "Boolean" Router Association Resources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. csv-table:: API methods for Router association resources :header: Method Name,Description,Input parameter(s),Output "list_bgpvpn_router_assocs()", "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_bgpvpn_router_assocs( BGPVPN UUID, param1=val1, param2=val2,...) (Optional)", "List of dictionaries of ROUTER ASSOCIATION attributes, one of each related to a given BGPVPN" "create_bgpvpn_router_assoc()", "Create a ROUTER ASSOCIATION resource for a given BGPVPN. 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", "Dictionary of ROUTER ASSOCIATION attributes" "show_bgpvpn_router_assoc()", "Get all parameters for a given ROUTER ASSOCIATION.", "1. UUID of the BGPVPN resource 2. UUID of the ROUTER ASSOCIATION resource", "Dictionary of ROUTER ASSOCIATION parameters" "update_bgpvpn_router_assoc()", "Update the parameters of the ROUTER ASSOCIATION resource provided as input.", "1. UUID of the BGPVPN resource 2. UUID of the ROUTER ASSOCIATION resource, 3. Dictionary of ROUTER ASSOCIATION parameters", "Dictionary of ROUTER ASSOCIATION parameters" "delete_bgpvpn_router_assoc()", "Delete a given ROUTER ASSOCIATION resource of which the UUID is provided as input.", "1. UUID of the BGPVPN resource 2. UUID of the ROUTER ASSOCIATION resource", "Boolean" Port Association Resources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. csv-table:: API methods for Port association resources :header: Method Name,Description,Input parameter(s),Output "list_bgpvpn_port_assocs()", "Get the list of defined PORT ASSOCIATION resources for a given BGPVPN. An optional list of PORT ASSOCIATION parameters can be used as filter.", "1. UUID of the BGPVPN 2. Use \**kwargs as filter, e.g. list_bgpvpn_port_assocs( BGPVPN UUID, param1=val1, param2=val2,...) (Optional)", "List of dictionaries of PORT ASSOCIATION attributes, one of each related to a given BGPVPN" "create_bgpvpn_port_assoc()", "Create a PORT ASSOCIATION resource for a given BGPVPN. Port UUID must be defined, provided in a PORT ASSOCIATION resource as input parameter.", "1. UUID of the said BGPVPN 2. Dictionary of PORT ASSOCIATION parameters", "Dictionary of PORT ASSOCIATION attributes" "show_bgpvpn_port_assoc()", "Get all parameters for a given PORT ASSOCIATION.", "1. UUID of the BGPVPN resource 2. UUID of the PORT ASSOCIATION resource", "Dictionary of PORT ASSOCIATION parameters" "update_bgpvpn_port_assoc()", "Update the parameters of the PORT ASSOCIATION resource provided as input.", "1. UUID of the BGPVPN resource 2. UUID of the PORT ASSOCIATION resource, 3. Dictionary of PORT ASSOCIATION parameters", "Dictionary of PORT ASSOCIATION parameters" "delete_bgpvpn_port_assoc()", "Delete a given PORT ASSOCIATION resource of which the UUID is provided as input.", "1. UUID of the BGPVPN resource 2. UUID of the PORT ASSOCIATION resource", "Boolean" Examples ~~~~~~~~ BGPVPN + Network Association Resources ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. literalinclude:: ../samples/bgpvpn-sample01.py networking-bgpvpn-12.0.0/doc/source/user/overview.rst0000664000175000017500000001055513656750513022741 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 ========================================== .. include:: ../introduction.rst 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. Unlike IPSec or SSL-based VPNs, for instance, 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-12.0.0/doc/source/user/drivers/0000775000175000017500000000000013656750625022015 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/user/drivers/opencontrail/0000775000175000017500000000000013656750625024512 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/user/drivers/opencontrail/index.rst0000664000175000017500000000704713656750513026357 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`__ [#]_. .. Note:: The BGPVPN Contrail driver that was under ``networking_bgpvpn`` (``networking_bgpvpn.neutron.services.service_drivers.opencontrail.opencontrail.OpenContrailBGPVPNDriver``) has been deprecated in Queens release, and has been completly removed in Stein release. The documentation below refers to the production ready `driver`_ under ``Juniper/contrail-neutron-plugin``. 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 https://opendev.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_ .. _route distinguisher: https://docs.openstack.org/api-ref/network/v2/#on-route-distinguishers-rds .. _router associations: https://docs.openstack.org/api-ref/network/v2/#router-associations .. _network associations: https://docs.openstack.org/api-ref/network/v2/#network-associations .. _association with ports: https://docs.openstack.org/api-ref/network/v2/#port-associations .. _devstack plugin: https://github.com/zioc/contrail-devstack-plugin networking-bgpvpn-12.0.0/doc/source/user/drivers/nuage/0000775000175000017500000000000013656750625023114 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/user/drivers/nuage/index.rst0000664000175000017500000000143213656750513024751 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-12.0.0/doc/source/user/drivers/opendaylight/0000775000175000017500000000000013656750625024504 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/user/drivers/opendaylight/index.rst0000664000175000017500000000262213656750513026343 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`_. .. note:: The legacy BGPVPN *v1 driver* for ODL that was hosted in ``networking-bgpvpn`` tree (``networking_bgpvpn.neutron.services.service_drivers.opendaylight.odl.OpenDaylightBgpvpnDriver``) has been deprecated in Rocky OpenStack release, and removed in Stein OpenStack release. The documentation below refers to the newer *v2 driver* in the ``networking-odl`` project. * add the following to local.conf to enable networking-odl plugin: .. code-block:: none enable_plugin networking-odl http://opendev.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_odl.bgpvpn.odl_v2.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-12.0.0/doc/source/user/drivers/index.rst0000664000175000017500000001222213656750513023651 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 | | | | (bagpipe) | [#]_ | [#]_ | | +=====================+========================+===========+==============+==============+=======+ | bgpvpn | base object | ✔ | ✔ | ✔ | ✔ | +---------------------+-------+----------------+-----------+--------------+--------------+-------+ | | | L3 | ✔ | ✔ | ✔ | ✔ | | | type +----------------+-----------+--------------+--------------+-------+ | | | L2 | ✔ | ✔ | ✔ | | | +-------+----------------+-----------+--------------+--------------+-------+ | | route_targets | ✔ | ✔ | ✔ | ✔ | +---------------------+------------------------+-----------+--------------+--------------+-------+ | | import_targets | ✔ | ✔ | ✔ | ✔ | | +------------------------+-----------+--------------+--------------+-------+ | | export_targets | ✔ | ✔ | ✔ | ✔ | | +------------------------+-----------+--------------+--------------+-------+ | | route_distinguishers | | | ✔ | ✔ | | +------------------------+-----------+--------------+--------------+-------+ | | vni | ✔ | | ✔ | | | +------------------------+-----------+--------------+--------------+-------+ | | local_pref | ✔ | | | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | network_association | base object | ✔ | ✔ | ✔ | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | router_association | base object | ✔ | ✔ | ✔ | ✔ | +---------------------+------------------------+-----------+--------------+--------------+-------+ | | advertise_extra_routes | | | [#]_ | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | port_association | base object | ✔ | | | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | | advertise_fixed_ips | ✔ | | | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | | routes:prefix | ✔ | | | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | | routes:bgpvpn | ✔ | | | | +---------------------+------------------------+-----------+--------------+--------------+-------+ | | routes:local_pref | ✔ | | | | +---------------------+------------------------+-----------+--------------+--------------+-------+ .. [#] This applies to the `current BGPVPN Contrail driver `_ sometimes called *v2 driver*, which is different from the now obsolete *v1 driver* that was under ``networking_bgpvpn``. .. [#] This applies to the `current BGPVPN ODL v2 driver `_ sometimes called *v2 driver*, which is different from the now obsolete *v1 driver* that was under ``networking_bgpvpn``. .. [#] The behavior corresponding to ``advertise_extra_routes: true``, is supported as the default with ODL, without support in the API for turning it off. networking-bgpvpn-12.0.0/doc/source/user/drivers/bagpipe/0000775000175000017500000000000013656750625023424 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/user/drivers/bagpipe/overview.blockdiag0000664000175000017500000000313413656750513027130 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-12.0.0/doc/source/user/drivers/bagpipe/index.rst0000664000175000017500000001204213656750513025260 0ustar zuulzuul00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode ================================ OVS/linuxbridge driver (bagpipe) ================================ Introduction ------------ The **BaGPipe** driver for the BGPVPN service plugin is designed to work jointly with the openvswitch and linuxbridge ML2 mechanism drivers. It relies on the use of the bagpipe-bgp BGP VPN implementation on compute nodes and the MPLS implementation in OpenVSwitch and or linuxbridge. Architecture overview --------------------- The bagpipe driver for the BGPVPN service plugin interacts with the Neutron 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. Example with the OpenVSwitch mechanism driver and agent: .. 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 https://opendev.org/openstack/networking-bagpipe.git # enable_plugin networking-bagpipe https://opendev.org/openstack/networking-bagpipe.git stable/pike # enable_plugin networking-bagpipe https://opendev.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 or linuxbridge 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 https://opendev.org/openstack/networking-bagpipe.git # enable_plugin networking-bagpipe https://opendev.org/openstack/networking-bagpipe.git stable/queens # enable_plugin networking-bagpipe https://opendev.org/openstack/networking-bagpipe.git stable/pike * 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=ovs BAGPIPE_DATAPLANE_DRIVER_EVPN=ovs # 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-12.0.0/doc/source/user/index.rst0000664000175000017500000000023013656750513022167 0ustar zuulzuul00000000000000================== User Documentation ================== .. toctree:: :maxdepth: 2 overview drivers/index usage horizon heat api networking-bgpvpn-12.0.0/doc/source/user/api.rst0000664000175000017500000000460113656750513021637 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-12.0.0/doc/source/index.rst0000664000175000017500000000103313656750513021213 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 ============================================= .. include:: introduction.rst .. only:: html Contents: .. toctree:: :maxdepth: 2 user/index install/index configuration/index contributor/index .. only:: html .. rubric:: Indices and Tables * :ref:`genindex` * :ref:`search` networking-bgpvpn-12.0.0/doc/source/contributor/0000775000175000017500000000000013656750625021733 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/contributor/future/0000775000175000017500000000000013656750625023245 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/contributor/future/index.rst0000664000175000017500000000012313656750513025076 0ustar zuulzuul00000000000000To be implemented ================= .. toctree:: :maxdepth: 2 attributes networking-bgpvpn-12.0.0/doc/source/contributor/future/attributes.rst0000664000175000017500000000411013656750513026155 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-12.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000011613656750513025166 0ustar zuulzuul00000000000000============ Contributing ============ .. include:: ../../../CONTRIBUTING.rst networking-bgpvpn-12.0.0/doc/source/contributor/index.rst0000664000175000017500000000017613656750513023574 0ustar zuulzuul00000000000000================= Contributor Guide ================= .. toctree:: :maxdepth: 2 contributing specs future/index networking-bgpvpn-12.0.0/doc/source/contributor/specs.rst0000664000175000017500000000077313656750513023605 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-12.0.0/doc/source/configuration/0000775000175000017500000000000013656750625022230 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/configuration/policy-sample.rst0000664000175000017500000000111413656750513025531 0ustar zuulzuul00000000000000==================================== Sample networking-bgpvpn Policy File ==================================== The following is a sample networking-bgpvpn policy file for adaptation and use. The sample policy can also be viewed in :download:`file form `. .. important:: The sample policy file is auto-generated from networking-bgpvpn when this documentation is built. You must ensure your version of networking-bgpvpn matches the version of this documentation. .. literalinclude:: /_static/networking-bgpvpn.policy.yaml.sample networking-bgpvpn-12.0.0/doc/source/configuration/networking-bgpvpn.rst0000664000175000017500000000132113656750513026434 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-12.0.0/doc/source/configuration/policy.rst0000664000175000017500000000047213656750513024260 0ustar zuulzuul00000000000000========================== networking-bgpvpn policies ========================== The following is an overview of all available policies in networking-bgpvpn. For a sample configuration file, refer to :doc:`/configuration/policy-sample`. .. show-policy:: :config-file: etc/oslo-policy-generator/policy.conf networking-bgpvpn-12.0.0/doc/source/configuration/samples/0000775000175000017500000000000013656750625023674 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/doc/source/configuration/samples/networking-bgpvpn.rst0000664000175000017500000000045613656750513030110 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-12.0.0/doc/source/configuration/index.rst0000664000175000017500000000134613656750513024071 0ustar zuulzuul00000000000000=================== Configuration Guide =================== Configuration ------------- This section provides a list of all possible options for each configuration file. networking-bgpvpn uses the following configuration file. .. toctree:: :maxdepth: 1 networking-bgpvpn The following is a sample configuration file for networking-bgpvpn. It is generated from code and reflect the current state of code in the networking-bgpvpn repository. .. toctree:: :maxdepth: 1 samples/networking-bgpvpn Policy ------ networking-bgpvpn, like most OpenStack projects, uses a policy language to restrict permissions on REST API actions. .. toctree:: :maxdepth: 1 Policy Reference Sample Policy File networking-bgpvpn-12.0.0/requirements.txt0000664000175000017500000000161613656750513020600 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>=4.0.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.db>=4.37.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 neutron-lib>=1.30.0 # Apache-2.0 debtcollector>=1.19.0 # Apache-2.0 # OpenStack CI will install the following projects from git # if they are in the required-projects list for a job: neutron>=13.0.0 # Apache-2.0 # The comment below indicates this project repo is current with neutron-lib # and should receive neutron-lib consumption patches as they are released # in neutron-lib. It also implies the project will stay current with TC # and infra initiatives ensuring consumption patches can land. # neutron-lib-current networking-bgpvpn-12.0.0/babel-django.cfg0000664000175000017500000000005713656750513020300 0ustar zuulzuul00000000000000[python: **.py] [django: **/templates/**.html] networking-bgpvpn-12.0.0/setup.cfg0000664000175000017500000000453613656750625017145 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-discuss@lists.openstack.org home-page = https://docs.openstack.org/networking-bgpvpn/latest/ python-requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 [files] packages = networking_bgpvpn networking_bgpvpn_heat bgpvpn_dashboard data_files = etc/neutron = etc/neutron/networking_bgpvpn.conf [extras] bagpipe = networking-bagpipe>=9.0.0 # Apache-2.0 horizon = horizon>=17.1.0 # Apache-2.0 [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 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 oslo.config.opts.defaults = networking-bgpvpn.service_provider = networking_bgpvpn.neutron.opts:set_service_provider_default oslo.policy.policies = networking-bgpvpn = networking_bgpvpn.policies:list_rules neutron.policies = networking-bgpvpn = networking_bgpvpn.policies:list_rules [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-12.0.0/bgpvpn_dashboard/0000775000175000017500000000000013656750625020617 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/__init__.py0000775000175000017500000000000013656750513022715 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/0000775000175000017500000000000013656750625022731 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/__init__.py0000775000175000017500000000000013656750513025027 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/0000775000175000017500000000000013656750625024377 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/__init__.py0000775000175000017500000000000013656750513026475 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/0000775000175000017500000000000013656750625025673 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/__init__.py0000775000175000017500000000000013656750513027771 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/panel.py0000775000175000017500000000144613656750513027350 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-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/0000775000175000017500000000000013656750625027671 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/0000775000175000017500000000000013656750625031165 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_modify.html0000775000175000017500000000035413656750513033502 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 %}././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_detail_overview.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_detail_overvie0000775000175000017500000000062213656750513034247 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" %}
././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associat0000775000175000017500000000000013656750625034314 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/_modify.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associat0000664000175000017500000000037113656750513034313 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 BGPVPN Router Association here." %}

{% endblock %} ././@LongLink0000000000000000000000000000017700000000000011222 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/_detail_overview.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associat0000664000175000017500000000155313656750513034316 0ustar zuulzuul00000000000000{% load i18n sizeformat %}
{% trans "Association ID" %}
{{router_association.id|default:_("None") }}
{% trans "Router Name" %}
{{ router_association.router_name|default:_("None") }}
{% trans "Router ID" %}
{{ router_association.router_id|default:_("None") }}
{% trans "Advertise Extra Routes" %}
{{router_association.advertise_extra_routes }}
././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/modify.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associat0000664000175000017500000000030713656750513034312 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update BGPVPN Router Association" %}{% endblock %} {% block main %} {% include "project/bgpvpn/_modify.html" %} {% endblock %} networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/index.html0000775000175000017500000000023313656750513033157 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "BGP VPNs" %}{% endblock %} {% block main %} {{ table.render }} {% endblock %}networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/modify.html0000775000175000017500000000027313656750513033343 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Update BGPVPN" %}{% endblock %} {% block main %} {% include "project/bgpvpn/_modify.html" %} {% endblock %} ././@LongLink0000000000000000000000000000016500000000000011217 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/create_network_association.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/create_network_0000664000175000017500000000032513656750513034257 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create Network Association" %}{% endblock %} {% block main %} {% include "project/bgpvpn/_create_network_association.html" %} {% endblock %} ././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/network_associations/networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/network_associa0000775000175000017500000000000013656750625034301 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000020000000000000011205 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/network_associations/_detail_overview.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/network_associa0000664000175000017500000000123713656750513034302 0ustar zuulzuul00000000000000{% load i18n sizeformat %}
{% trans "Association ID" %}
{{network_association.id|default:_("None") }}
{% trans "Network Name" %}
{{ network_association.network_name|default:_("None") }}
{% trans "Network ID" %}
{{ network_association.network_id|default:_("None") }}
././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_routers.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_rou0000664000175000017500000000060213656750513034245 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 %}././@LongLink0000000000000000000000000000016600000000000011220 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_create_network_association.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_create_network0000664000175000017500000000031113656750513034252 0ustar zuulzuul00000000000000{% extends "horizon/common/_modal_form.html" %} {% load i18n %} {% block modal-body-right %}

{% trans "Description:" %}

{% trans "Associate a BGPVPN to a Network" %}

{% endblock %}././@LongLink0000000000000000000000000000015700000000000011220 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_networks.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/_associated_net0000775000175000017500000000064213656750513034235 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 %}networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/urls.py0000775000175000017500000000525613656750513027241 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 include from django.conf.urls import url from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations import \ urls as network_associations_urls from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations import \ views as network_associations_views from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations import \ urls as router_associations_urls from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations import \ views as router_associations_views from bgpvpn_dashboard.dashboards.project.bgpvpn import 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 % 'create-network-association', bgpvpn_views.CreateNetworkAssociationView.as_view(), name='create-network-association'), url(BGPVPN % 'create-router-association', bgpvpn_views.CreateRouterAssociationView.as_view(), name='create-router-association'), url(r'^(?P[^/]+)/detail/$', bgpvpn_views.DetailProjectView.as_view(), name='detail'), url(r'^(?P[^/]+)/network_assos/' r'(?P[^/]+)/' r'detail\?tab=bgpvpns__network__associations_tab$', network_associations_views.DetailView.as_view(), name='network_associations_tab'), url(r'^(?P[^/]+)/router_assos/(?P[^/]+)/' r'detail\?tab=bgpvpns__router_associations_tab$', router_associations_views.DetailView.as_view(), name='router_associations_tab'), url(r'^(?P[^/]+)/router_assos/(?P[^/]+)/' r'update$', router_associations_views.UpdateRouterAssociationsView.as_view(), name='update-router-association'), url(r'^network_assos/', include((network_associations_urls, 'network_assos'))), url(r'^router_assos/', include((router_associations_urls, 'router_assos'))), ] networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/0000775000175000017500000000000013656750625031772 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/__init__.py0000664000175000017500000000000013656750513034065 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/urls.py0000664000175000017500000000177313656750513033335 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. from django.conf.urls import url import bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations.views \ as bgpvpn_router_associations_views ROUTER_ASSO = r'^(?P[^/]+)/router_assos/' \ r'(?P[^/]+)/%s$' urlpatterns = [ url(ROUTER_ASSO % 'detail', bgpvpn_router_associations_views.DetailView.as_view(), name='detail'), ] networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/tabs.py0000664000175000017500000000603013656750513033270 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. from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs from horizon.utils import memoized from openstack_dashboard import api from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations \ import tables as router_tables class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "project/bgpvpn/router_associations/_detail_overview.html" def get_context_data(self, request): router_association = self.tab_group.kwargs['router_association'] router = self.get_router(router_association.router_id) router_association.router_name = router.get('name') router_association.router_url = self.get_router_detail_url( router_association.router_id) return {'router_association': router_association} @memoized.memoized_method def get_router(self, router_id): try: router = api.neutron.router_get(self.request, router_id) except Exception: router = {} msg = _('Unable to retrieve router details.') exceptions.handle(self.request, msg) return router @staticmethod def get_router_detail_url(router_id): return reverse('horizon:project:routers:detail', args=(router_id,)) class RouterAssociationsTab(tabs.TableTab): name = _("Router Associations") slug = "router_associations_tab" table_classes = (router_tables.RouterAssociationsTable,) template_name = ("horizon/common/_detail_table.html") preload = False def get_router_associations_data(self): try: bgpvpn_id = self.tab_group.kwargs['bgpvpn_id'] router_associations = bgpvpn_api.router_association_list( self.request, bgpvpn_id) for router_asso in router_associations: router = api.neutron.router_get(self.request, router_asso.router_id) router_asso.router_name = router.name except Exception: router_associations = [] msg = _('Router associations list can not be retrieved.') exceptions.handle(self.request, msg) return router_associations class RouterAssociationDetailTabs(tabs.TabGroup): slug = "router_association_details" tabs = (OverviewTab,) networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/views.py0000664000175000017500000000747713656750513033514 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. from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import tabs from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations \ import forms as bgpvpn_router_associations_forms from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations \ import tabs as project_tabs class UpdateRouterAssociationsView(forms.ModalFormView): form_class = bgpvpn_router_associations_forms.UpdateRouterAssociation form_id = "update_router_association_form" modal_header = _("Update BGPVPN Router Associations") submit_label = _("Update") submit_url = 'horizon:project:bgpvpn:update-router-association' template_name = 'project/bgpvpn/router_associations/modify.html' page_title = _("Update BGPVPN Router Association") url = 'horizon:project:bgpvpn:detail' def get_success_url(self): return reverse(self.url, args=(self.kwargs['bgpvpn_id'],)) def get_context_data(self, **kwargs): context = super( UpdateRouterAssociationsView, self).get_context_data(**kwargs) args = (self.kwargs['bgpvpn_id'], self.kwargs['router_association_id']) context["bgpvpn_id"] = self.kwargs['bgpvpn_id'] context["router_association_id"] = self.kwargs['router_association_id'] context["submit_url"] = reverse(self.submit_url, args=args) return context def get_initial(self): bgpvpn_id = self.kwargs['bgpvpn_id'] router_association_id = self.kwargs['router_association_id'] try: router_association = bgpvpn_api.router_association_get( self.request, bgpvpn_id, router_association_id) except Exception: exceptions.handle( self.request, _('Unable to retrieve Router Association details.'), redirect=self.success_url) else: data = router_association.to_dict() data['router_association_id'] = data.pop('id') data['bgpvpn_id'] = bgpvpn_id return data class DetailView(tabs.TabView): tab_group_class = project_tabs.RouterAssociationDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ router_association.id }}" def get_data(self): router_association_id = self.kwargs['router_association_id'] bgpvpn_id = self.kwargs['bgpvpn_id'] try: router_association = bgpvpn_api.router_association_get( self.request, bgpvpn_id, router_association_id) return router_association except Exception: router_association = [] msg = _('Unable to retrieve router association details.') exceptions.handle(self.request, msg, redirect=self.get_redirect_url()) return router_association def get_tabs(self, request, *args, **kwargs): router_association = self.get_data() return self.tab_group_class( request, router_association=router_association, **kwargs) @staticmethod def get_redirect_url(): return reverse('horizon:project:bgpvpn:index') networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/tables.py0000664000175000017500000001010513656750513033607 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 logging from django.urls import reverse from django.urls import reverse_lazy from django.utils import safestring 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 api from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api LOG = logging.getLogger(__name__) class DeleteRouterAssociation(tables.DeleteAction): @staticmethod def action_present(count): return ungettext_lazy(u"Delete Router Association", u"Delete Router Associations", count) @staticmethod def action_past(count): return ungettext_lazy(u"Deleted Router Association", u"Deleted Router Associations", count) def delete(self, request, asso_id): try: bgpvpn_api.router_association_delete( request, asso_id, self.table.kwargs['bgpvpn_id']) except Exception: msg = _('Failed to delete Router Association %s') % asso_id LOG.info(msg) redirect = reverse('horizon:project:bgpvpn:detail') exceptions.handle(request, msg, redirect=redirect) class UpdateRouterAssociation(tables.LinkAction): name = "update" verbose_name = _("Update Router Association") url = "horizon:project:bgpvpn:update-router-association" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, asso): bgpvpn_id = self.table.kwargs['bgpvpn_id'] return reverse(self.url, args=(bgpvpn_id, asso.id)) def allowed(self, request, datum=None): if api.neutron.is_extension_supported(request, 'bgpvpn-routes-control'): return True class CreateRouterAssociation(tables.LinkAction): name = "create_router_association" verbose_name = _("Create Router Association") url = "horizon:project:bgpvpn:create-router-association" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, datum=None): bgpvpn_id = self.table.kwargs['bgpvpn_id'] return reverse(self.url, args=(bgpvpn_id,)) class IDColumn(tables.Column): url = "horizon:project:bgpvpn:router_assos:detail" def get_link_url(self, asso): bgpvpn_id = self.table.kwargs['bgpvpn_id'] return reverse(self.url, args=(bgpvpn_id, asso.id)) class RouterColumn(tables.Column): def get_raw_data(self, asso): url = reverse('horizon:project:routers:detail', args=[asso.router_id]) instance = '%s' % (url, asso.router_name or asso.router_id) return safestring.mark_safe(instance) class RouterAssociationsTable(tables.DataTable): id = IDColumn("id", verbose_name=_("Association ID"), link='horizon:project:bgpvpn:router_assos:detail') router = RouterColumn("router_id", verbose_name=_("Router")) failure_url = reverse_lazy('horizon:project:bgpvpn:detail') class Meta(object): name = "router_associations" verbose_name = _("Router Associations") table_actions = (CreateRouterAssociation, DeleteRouterAssociation) row_actions = (UpdateRouterAssociation, DeleteRouterAssociation,) hidden_title = False def get_object_display(self, asso): return asso.id networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/forms.py0000664000175000017500000000433513656750513033473 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 logging from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api LOG = logging.getLogger(__name__) class UpdateRouterAssociation(forms.SelfHandlingForm): bgpvpn_id = forms.CharField(widget=forms.HiddenInput()) router_association_id = forms.CharField( label=_("ID"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) router_id = forms.CharField( label=_("Router"), widget=forms.TextInput(attrs={'readonly': 'readonly'})) advertise_extra_routes = forms.BooleanField( label=_("Advertise Extra Routes"), initial=True, required=False, help_text="Boolean flag controlling whether or not the routes " "specified in the routes attribute of the router will be " "advertised to the BGPVPN (default: true).") def __init__(self, request, *args, **kwargs): super(UpdateRouterAssociation, self).__init__(request, *args, **kwargs) self.action = 'update' def handle(self, request, data): router_association_id = data['router_association_id'] bgpvpn_id = data['bgpvpn_id'] try: bgpvpn_api.router_association_update( request, bgpvpn_id, router_association_id, advertise_extra_routes=data['advertise_extra_routes']) return True except exceptions as e: exceptions.handle( request, "Unable to update bgpvpn router association %s:" % str(e)) return False networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/0000775000175000017500000000000013656750625032143 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/__init__.py0000664000175000017500000000000013656750513034236 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/urls.py0000664000175000017500000000201013656750513033467 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. from django.conf.urls import url from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations \ import views as bgpvpn_network_associations_views NETWORK_ASSO = r'^(?P[^/]+)/network_assos/' \ r'(?P[^/]+)/%s$' urlpatterns = [ url(NETWORK_ASSO % 'detail', bgpvpn_network_associations_views.DetailView.as_view(), name='detail'), ] networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/tabs.py0000664000175000017500000000611613656750513033446 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. from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs from horizon.utils import memoized from openstack_dashboard import api from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations \ import tables as network_tables class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = "project/bgpvpn/network_associations/_detail_overview.html" def get_context_data(self, request): network_association = self.tab_group.kwargs['network_association'] network = self.get_network(network_association.network_id) network_association.network_name = network.get('name') network_association.network_url = self.get_network_detail_url( network_association.network_id) return {'network_association': network_association} @memoized.memoized_method def get_network(self, network_id): try: network = api.neutron.network_get(self.request, network_id) except Exception: network = {} msg = _('Unable to retrieve network details.') exceptions.handle(self.request, msg) return network @staticmethod def get_network_detail_url(network_id): return reverse('horizon:project:networks:detail', args=(network_id,)) class NetworkAssociationsTab(tabs.TableTab): name = _("Network Associations") slug = "network_associations_tab" table_classes = (network_tables.NetworkAssociationsTable,) template_name = ("horizon/common/_detail_table.html") preload = False def get_network_associations_data(self): try: bgpvpn_id = self.tab_group.kwargs['bgpvpn_id'] network_associations = bgpvpn_api.network_association_list( self.request, bgpvpn_id) for network_asso in network_associations: network = api.neutron.network_get(self.request, network_asso.network_id) network_asso.network_name = network.name except Exception: network_associations = [] msg = _('Network associations list can not be retrieved.') exceptions.handle(self.request, msg) return network_associations class NetworkAssociationDetailTabs(tabs.TabGroup): slug = "network_association_details" tabs = (OverviewTab,) networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/views.py0000664000175000017500000000452713656750513033656 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. from django.urls import reverse from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations \ import tabs as project_tabs class DetailView(tabs.TabView): tab_group_class = project_tabs.NetworkAssociationDetailTabs template_name = 'horizon/common/_detail.html' page_title = "{{ network_association.id }}" def get_data(self): network_association_id = self.kwargs['network_association_id'] bgpvpn_id = self.kwargs['bgpvpn_id'] try: network_association = bgpvpn_api.network_association_get( self.request, bgpvpn_id, network_association_id) return network_association except Exception: network_association = [] msg = _('Unable to retrieve network association details.') exceptions.handle(self.request, msg, redirect=self.get_redirect_url()) return network_association def get_context_data(self, **kwargs): context = super(DetailView, self).get_context_data(**kwargs) network_association = self.get_data() context["url"] = reverse( "horizon:project:bgpvpn:network_associations_tab", args=(self.kwargs["bgpvpn_id"], network_association.id)) return context def get_tabs(self, request, *args, **kwargs): network_association = self.get_data() return self.tab_group_class( request, network_association=network_association, **kwargs) @staticmethod def get_redirect_url(): return reverse('horizon:project:bgpvpn:index') networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/tables.py0000664000175000017500000000666113656750513033774 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 logging from django.urls import reverse from django.urls import reverse_lazy from django.utils import safestring from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ungettext_lazy from horizon import exceptions from horizon import tables from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api LOG = logging.getLogger(__name__) class DeleteNetworkAssociation(tables.DeleteAction): @staticmethod def action_present(count): return ungettext_lazy(u"Delete Network Association", u"Delete Network Associations", count) @staticmethod def action_past(count): return ungettext_lazy(u"Deleted Network Association", u"Deleted Network Associations", count) def delete(self, request, asso_id): try: bgpvpn_api.network_association_delete( request, asso_id, self.table.kwargs['bgpvpn_id']) except Exception: msg = _('Failed to delete Network Association %s') % asso_id LOG.info(msg) redirect = reverse('horizon:project:bgpvpn:detail') exceptions.handle(request, msg, redirect=redirect) class CreateNetworkAssociation(tables.LinkAction): name = "create_network_association" verbose_name = _("Create Network Association") url = "horizon:project:bgpvpn:create-network-association" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, datum=None): bgpvpn_id = self.table.kwargs['bgpvpn_id'] return reverse(self.url, args=(bgpvpn_id,)) class IDColumn(tables.Column): url = "horizon:project:bgpvpn:network_assos:detail" def get_link_url(self, asso): bgpvpn_id = self.table.kwargs['bgpvpn_id'] return reverse(self.url, args=(bgpvpn_id, asso.id)) class NetworkColumn(tables.Column): def get_raw_data(self, asso): url = reverse('horizon:project:networks:detail', args=[asso.network_id]) instance = '%s' % (url, asso.network_name or asso.network_id) return safestring.mark_safe(instance) class NetworkAssociationsTable(tables.DataTable): id = IDColumn("id", verbose_name=_("Association ID"), link='horizon:project:bgpvpn:network_assos:detail') network = NetworkColumn("network_id", verbose_name=_("Network")) failure_url = reverse_lazy('horizon:project:bgpvpn:detail') class Meta(object): name = "network_associations" verbose_name = _("Network Associations") table_actions = (CreateNetworkAssociation,) row_actions = (DeleteNetworkAssociation,) hidden_title = False def get_object_display(self, asso): return asso.id networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/tabs.py0000664000175000017500000000400113656750513027165 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 bgpvpn_dashboard.api.bgpvpn as bgpvpn_api from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import tabs from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations \ import tabs as network_tabs from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations \ import tabs as router_tabs class OverviewTab(tabs.Tab): name = _("Overview") slug = "overview" template_name = ("project/bgpvpn/_detail_overview.html") preload = False def _get_data(self): bgpvpn = {} bgpvpn_id = None try: bgpvpn_id = self.tab_group.kwargs['bgpvpn_id'] bgpvpn = bgpvpn_api.bgpvpn_get(self.request, bgpvpn_id) bgpvpn.set_id_as_name_if_empty(length=0) except Exception: msg = _('Unable to retrieve details for bgpvpn "%s".') % ( bgpvpn_id) exceptions.handle(self.request, msg) return bgpvpn def get_context_data(self, request, **kwargs): context = super(OverviewTab, self).get_context_data(request, **kwargs) bgpvpn = self._get_data() context["bgpvpn"] = bgpvpn return context class BgpvpnDetailsTabs(tabs.DetailTabsGroup): slug = "bgpvpn_tabs" tabs = (OverviewTab, network_tabs.NetworkAssociationsTab, router_tabs.RouterAssociationsTab) sticky = True networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/workflows.py0000775000175000017500000001332613656750513030306 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.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import workflows from openstack_dashboard import api LOG = logging.getLogger(__name__) from bgpvpn_dashboard.api import bgpvpn as bgpvpn_api class AddRouterParametersInfoAction(workflows.Action): advertise_extra_routes = forms.BooleanField( label=_("Advertise Extra Routes"), initial=True, required=False, help_text="Boolean flag controlling whether or not the routes " "specified in the routes attribute of the router will be " "advertised to the BGPVPN (default: true).") class Meta(object): name = _("Optional Parameters") slug = "add_router_parameters" def __init__(self, request, context, *args, **kwargs): super(AddRouterParametersInfoAction, self).__init__( request, context, *args, **kwargs) if 'with_parameters' in context: self.fields['with_parameters'] = forms.BooleanField( initial=context['with_parameters'], required=False, widget=forms.HiddenInput() ) class CreateRouterAssociationInfoAction(workflows.Action): router_resource = forms.ChoiceField( label=_("Associate Router"), widget=forms.ThemableSelectWidget( data_attrs=('name', 'id'), transform=lambda x: "%s" % x.name_or_id)) class Meta(object): name = _("Create Association") help_text = _("Create a new router association.") slug = "create_router_association" def __init__(self, request, context, *args, **kwargs): super(CreateRouterAssociationInfoAction, self).__init__( request, context, *args, **kwargs) # 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("project_id"): tenant_id = context.get("project_id") else: tenant_id = self.request.user.tenant_id try: routers = api.neutron.router_list(request, tenant_id=tenant_id) if routers: choices = [('', _("Choose a router"))] + [(r.id, r) for r in routers] self.fields['router_resource'].choices = choices else: self.fields['router_resource'].choices = [('', _("No router"))] except Exception: exceptions.handle(request, _("Unable to retrieve routers")) if api.neutron.is_extension_supported(request, 'bgpvpn-routes-control'): self.fields['with_parameters'] = forms.BooleanField( label=_("Optional parameters"), initial=False, required=False, widget=forms.CheckboxInput(attrs={ 'class': 'switchable', 'data-hide-tab': 'router_association__' 'add_router_parameters', 'data-hide-on-checked': 'false' })) class AddRouterParametersInfo(workflows.Step): action_class = AddRouterParametersInfoAction depends_on = ("bgpvpn_id", "name") contributes = ("advertise_extra_routes",) class CreateRouterAssociationInfo(workflows.Step): action_class = CreateRouterAssociationInfoAction contributes = ("router_resource", "with_parameters") class RouterAssociation(workflows.Workflow): slug = "router_association" name = _("Associate a BGPVPN to a Router") finalize_button_name = _("Create") success_message = _('Router association with "%s" created.') failure_message = _('Unable to create a router association with "%s".') success_url = "horizon:project:bgpvpn:index" default_steps = (CreateRouterAssociationInfo, AddRouterParametersInfo) wizard = True def format_status_message(self, message): name = self.context['name'] or self.context['bgpvpn_id'] return message % name def handle(self, request, context): bgpvpn_id = context['bgpvpn_id'] router_id = context["router_resource"] msg_error = _("Unable to associate router %s") % router_id try: router_association = bgpvpn_api.router_association_create( request, bgpvpn_id, router_id=router_id) except Exception as e: exceptions.handle(request, msg_error + ": %s" % str(e)) return False if not context["with_parameters"]: return True asso_id = router_association['router_association']['id'] try: bgpvpn_api.router_association_update( request, bgpvpn_id, asso_id, advertise_extra_routes=context['advertise_extra_routes']) return True except exceptions as e: bgpvpn_api.router_association_delete(request, asso_id, bgpvpn_id) exceptions.handle(request, msg_error + ": %s" % str(e)) return False networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/views.py0000775000175000017500000001411513656750513027403 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.urls import reverse from django.urls 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 import tabs from horizon.utils import memoized from horizon import workflows from openstack_dashboard import api from bgpvpn_dashboard.api import 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 tabs as bgpvpn_tabs 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 GetBgpvpnMixin(object): 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.success_url) class CreateNetworkAssociationView(GetBgpvpnMixin, forms.ModalFormView): form_class = bgpvpn_forms.CreateNetworkAssociation form_id = "create_network_association_form" modal_header = _("Create Network Association") submit_label = _("Create") submit_url = 'horizon:project:bgpvpn:create-network-association' success_url = reverse_lazy('horizon:project:bgpvpn:index') template_name = 'project/bgpvpn/create_network_association.html' page_title = _("Create Network Association") def get_context_data(self, **kwargs): context = super( CreateNetworkAssociationView, 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 class CreateRouterAssociationView(GetBgpvpnMixin, workflows.WorkflowView): workflow_class = bgpvpn_workflows.RouterAssociation page_title = _("Create Router associations") failure_url = reverse_lazy("horizon:project:bgpvpn:index") class DetailProjectView(tabs.TabbedTableView): tab_group_class = bgpvpn_tabs.BgpvpnDetailsTabs template_name = 'horizon/common/_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-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/tables.py0000775000175000017500000000615613656750513027526 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.urls 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 CreateNetworkAssociation(tables.LinkAction): name = "create_network_association" verbose_name = _("Create Network Association") url = "horizon:project:bgpvpn:create-network-association" classes = ("ajax-modal",) icon = "pencil" class CreateRouterAssociation(tables.LinkAction): name = "create_router_association" verbose_name = _("Create Router Association") url = "horizon:project:bgpvpn:create-router-association" classes = ("ajax-modal",) icon = "pencil" def get_link_url(self, bgpvpn): step = 'create_router_association' 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, CreateNetworkAssociation, CreateRouterAssociation) networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/project/bgpvpn/forms.py0000775000175000017500000001373213656750513027400 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.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _ from horizon import exceptions from horizon import forms from horizon import messages from openstack_dashboard import api 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' class CreateNetworkAssociation(forms.SelfHandlingForm): bgpvpn_id = forms.CharField(widget=forms.HiddenInput()) network_resource = forms.ChoiceField( label=_("Associate Network"), widget=forms.ThemableSelectWidget( data_attrs=('name', 'id'), transform=lambda x: "%s" % x.name_or_id)) def __init__(self, request, *args, **kwargs): super(CreateNetworkAssociation, self).__init__( request, *args, **kwargs) # when an admin user uses the project panel BGPVPN, there is no # project in data because bgpvpn_get doesn't return it project_id = kwargs.get('initial', {}).get("project_id", None) if request.user.is_superuser and project_id: tenant_id = project_id else: tenant_id = self.request.user.tenant_id try: networks = api.neutron.network_list_for_tenant(request, tenant_id) if networks: choices = [('', _("Choose a network"))] + [(n.id, n) for n in networks] self.fields['network_resource'].choices = choices else: self.fields['network_resource'].choices = [('', _("No network"))] except Exception as e: exceptions.handle( request, _("Unable to retrieve networks: %s") % e) def handle(self, request, data): try: params = self._set_params(data) bgpvpn_api.network_association_create( request, data['bgpvpn_id'], **params) return True except Exception as e: exceptions.handle( request, _("Unable to associate network {} : {}").format( data["network_resource"], str(e))) return False def _set_params(self, data): params = dict() params['network_id'] = data['network_resource'] return params networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/0000775000175000017500000000000013656750625024021 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/__init__.py0000775000175000017500000000000013656750513026117 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/0000775000175000017500000000000013656750625025315 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/__init__.py0000775000175000017500000000000013656750513027413 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/panel.py0000775000175000017500000000144613656750513026772 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-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/0000775000175000017500000000000013656750625027313 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/0000775000175000017500000000000013656750625030607 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/create.html0000775000175000017500000000026213656750513032737 0ustar zuulzuul00000000000000{% extends 'base.html' %} {% load i18n %} {% block title %}{% trans "Create BGPVPN" %}{% endblock %} {% block main %} {% include "admin/bgpvpn/_create.html" %} {% endblock %} ././@LongLink0000000000000000000000000000015100000000000011212 Lustar 00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_detail_overview.htmlnetworking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_detail_overview.0000775000175000017500000000165213656750513034142 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-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/templates/bgpvpn/_create.html0000775000175000017500000000054513656750513033102 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-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/urls.py0000775000175000017500000000260413656750513026655 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 % 'create-network-association', bgpvpn_views.CreateNetworkAssociationView.as_view(), name='create-network-association'), url(BGPVPN % 'create-router-association', bgpvpn_views.CreateRouterAssociationView.as_view(), name='create-router-association'), url(r'^(?P[^/]+)/detail/$', bgpvpn_views.DetailProjectView.as_view(), name='detail'), ] networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/tabs.py0000664000175000017500000000224713656750513026621 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. from bgpvpn_dashboard.dashboards.project.bgpvpn.network_associations \ import tabs as network_tabs from bgpvpn_dashboard.dashboards.project.bgpvpn.router_associations \ import tabs as router_tabs from bgpvpn_dashboard.dashboards.project.bgpvpn import tabs as project_tabs class OverviewTab(project_tabs.OverviewTab): template_name = "admin/bgpvpn/_detail_overview.html" class BgpvpnDetailsTabs(project_tabs.BgpvpnDetailsTabs): tabs = (OverviewTab, network_tabs.NetworkAssociationsTab, router_tabs.RouterAssociationsTab) networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/workflows.py0000775000175000017500000000205513656750513027725 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 RouterAssociation(project_workflows.RouterAssociation): success_url = "horizon:admin:bgpvpn:index" def _set_params(self, data, association_type, resource): params = super(RouterAssociation, self)._set_params( data, association_type, resource) params['tenant_id'] = data['tenant_id'] return params networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/views.py0000775000175000017500000000647713656750513027041 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.urls 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 tabs as bgpvpn_tabs 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 CreateNetworkAssociationView(project_views.CreateNetworkAssociationView): form_class = bgpvpn_forms.CreateNetworkAssociation submit_url = 'horizon:admin:bgpvpn:create-network-association' success_url = reverse_lazy('horizon:admin:bgpvpn:index') class CreateRouterAssociationView(project_views.CreateRouterAssociationView): workflow_class = bgpvpn_workflows.RouterAssociation failure_url = reverse_lazy("horizon:admin:bgpvpn:index") class DetailProjectView(project_views.DetailProjectView): tab_group_class = bgpvpn_tabs.BgpvpnDetailsTabs redirect_url = 'horizon:admin:bgpvpn:index' networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/tables.py0000775000175000017500000000744513656750513027152 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.urls 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 CreateNetworkAssociation(project_tables.CreateNetworkAssociation): url = "horizon:admin:bgpvpn:create-network-association" class CreateRouterAssociation(project_tables.CreateRouterAssociation): url = "horizon:admin:bgpvpn:create-router-association" 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(project_tables.BgpvpnTable): 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")) class Meta(object): table_actions = (CreateBgpVpn, DeleteBgpvpn) row_actions = (EditInfoBgpVpn, CreateNetworkAssociation, CreateRouterAssociation, DeleteBgpvpn) columns = ("tenant_id", "name", "type", "route_targets", "import_targets", "export_targets", "networks", "routers") networking-bgpvpn-12.0.0/bgpvpn_dashboard/dashboards/admin/bgpvpn/forms.py0000775000175000017500000001051413656750513027015 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.validators import RegexValidator from django.urls import reverse_lazy 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' class CreateNetworkAssociation(project_forms.CreateNetworkAssociation): project_id = forms.CharField(widget=forms.HiddenInput()) def _set_params(self, data): params = super(CreateNetworkAssociation, self)._set_params(data) params['tenant_id'] = data['project_id'] return params networking-bgpvpn-12.0.0/bgpvpn_dashboard/common/0000775000175000017500000000000013656750625022107 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/common/__init__.py0000775000175000017500000000000013656750513024205 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/common/bgpvpn.py0000775000175000017500000000211713656750513023755 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-12.0.0/bgpvpn_dashboard/etc/0000775000175000017500000000000013656750625021372 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/etc/bgpvpn-horizon.conf0000664000175000017500000000370413656750513025223 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-12.0.0/bgpvpn_dashboard/enabled/0000775000175000017500000000000013656750625022211 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/enabled/__init__.py0000775000175000017500000000000013656750513024307 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/enabled/_2360_admin_bgpvpn_panel.py0000775000175000017500000000065013656750513027217 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-12.0.0/bgpvpn_dashboard/enabled/_1495_project_bgpvpn_panel.py0000775000175000017500000000065413656750513027611 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-12.0.0/bgpvpn_dashboard/api/0000775000175000017500000000000013656750625021370 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/api/__init__.py0000775000175000017500000000000013656750513023466 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/api/bgpvpn.py0000775000175000017500000001313013656750513023233 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} client = neutronclient(request) bgpvpn = client.create_bgpvpn(body=body).get('bgpvpn') 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_get(request, bgpvpn_id, network_assoc_id, **kwargs): LOG.debug("network_association_get(): " "bgpvpn_id=%s, network_assoc_id=%s, kwargs=%s", bgpvpn_id, network_assoc_id, kwargs) network_association = neutronclient(request).show_bgpvpn_network_assoc( bgpvpn_id, network_assoc_id).get('network_association') return NetworkAssociation(network_association) 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_bgpvpn_network_assocs( 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_bgpvpn_network_assoc(bgpvpn_id, body=body) .get('network_association')) 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_bgpvpn_network_assoc(bgpvpn_id, resource_id) def router_association_get(request, bgpvpn_id, router_assoc_id, **kwargs): LOG.debug("router_association_get(): " "bgpvpn_id=%s, router_assoc_id=%s, kwargs=%s", bgpvpn_id, router_assoc_id, kwargs) router_association = neutronclient(request).show_bgpvpn_router_assoc( bgpvpn_id, router_assoc_id).get('router_association') return RouterAssociation(router_association) 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_bgpvpn_router_assocs(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_bgpvpn_router_assoc( bgpvpn_id, body=body).get('router_association') return RouterAssociation(router_associations) def router_association_update(request, bgpvpn_id, router_association_id, **kwargs): LOG.debug("router_association_update(): bgpvpnid=%s " "router_association_id=%s params=%s", bgpvpn_id, router_association_id, kwargs) body = {'router_association': kwargs} router_associations = neutronclient(request).update_bgpvpn_router_assoc( bgpvpn_id, router_association_id, body=body).get('router_association') 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_bgpvpn_router_assoc(bgpvpn_id, resource_id) networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/0000775000175000017500000000000013656750625021576 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/__init__.py0000775000175000017500000000000013656750513023674 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/test_data/0000775000175000017500000000000013656750625023546 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/test_data/utils.py0000775000175000017500000000223313656750513025257 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-12.0.0/bgpvpn_dashboard/test/test_data/__init__.py0000775000175000017500000000000013656750513025644 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/test_data/bgpvpn_data.py0000775000175000017500000000400313656750513026401 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-12.0.0/bgpvpn_dashboard/test/project/0000775000175000017500000000000013656750625023244 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/project/__init__.py0000775000175000017500000000000013656750513025342 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/project/test_tables.py0000664000175000017500000000620713656750513026130 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 django import test from django.utils.translation import ugettext_lazy as _ from bgpvpn_dashboard.dashboards.project.bgpvpn import tables as bgpvpn_tables class TestFunctionGet(test.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(test.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(test.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-12.0.0/bgpvpn_dashboard/test/project/test_views.py0000664000175000017500000001367413656750513026021 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.urls import reverse from django.urls 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 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) networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/project/test_forms.py0000664000175000017500000000350213656750513025777 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-12.0.0/bgpvpn_dashboard/test/api_tests/0000775000175000017500000000000013656750625023571 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/api_tests/__init__.py0000775000175000017500000000000013656750513025667 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/api_tests/test_bgpvpn.py0000775000175000017500000002060613656750513026501 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 mock 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 class BgpvpnApiTests(bgpvpn_test.APITestCase): def setUp(self): bgpvpn_test.APITestCase.setUp(self) # Since the early days of networking-bgpvpn, the package was providing # a neutronclient.extensions entry point so that neutronclient would # dynamically add BGPVPN API methods to a Client instance. This is # only kept for backward compatibility, but not required anymore for # users of >=Ocata python-neutronclient. However, the dynamic addition # of these methods makes is a pain to mock. The patch below disables # the addition of the dynamic BGPVPN methods. mock.patch('neutronclient.v2_0.client.Client._register_extensions' ).start() @test.create_mocks({neutronclient: ('list_bgpvpns',)}) def test_bgpvpn_list(self): exp_bgpvpns = self.bgpvpns.list() api_bgpvpns = {'bgpvpns': self.api_bgpvpns.list()} self.mock_list_bgpvpns.return_value = api_bgpvpns ret_vals = api.bgpvpn.bgpvpns_list(self.request) for (ret_val, exp_bgpvpn) in zip(ret_vals, exp_bgpvpns): self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) self.assertEqual(exp_bgpvpn.id, ret_val.id) self.assertEqual(exp_bgpvpn.name, ret_val.name) @test.create_mocks({neutronclient: ('create_bgpvpn',)}) 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} self.mock_create_bgpvpn.return_value = ret_dict ret_val = api.bgpvpn.bgpvpn_create(self.request, **data) self.mock_create_bgpvpn.assert_called_once_with(body=ret_dict) self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) self.assertEqual(bgpvpn.name, ret_val.name) @test.create_mocks({neutronclient: ('show_bgpvpn',)}) def test_bgpvpn_get(self): bgpvpn = self.bgpvpns.first() ret_dict = {'bgpvpn': self.api_bgpvpns.first()} self.mock_show_bgpvpn.return_value = ret_dict ret_val = api.bgpvpn.bgpvpn_get(self.request, bgpvpn.id) self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) self.assertEqual(bgpvpn.name, ret_val.name) @test.create_mocks({neutronclient: ('update_bgpvpn',)}) 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'] data = {'name': bgpvpn.name, 'route_targets': bgpvpn.route_targets} ret_dict = {'bgpvpn': bgpvpn_dict} self.mock_update_bgpvpn.return_value = ret_dict ret_val = api.bgpvpn.bgpvpn_update(self.request, bgpvpn.id, **data) self.assertIsInstance(ret_val, api.bgpvpn.Bgpvpn) self.assertEqual('new name', ret_val.name) self.assertEqual(['65001:2'], ret_val.route_targets) @test.create_mocks({neutronclient: ('delete_bgpvpn',)}) def test_bgpvpn_delete(self): bgpvpn = self.bgpvpns.first() api.bgpvpn.bgpvpn_delete(self.request, bgpvpn.id) self.mock_delete_bgpvpn.assert_called_once_with(bgpvpn.id) @test.create_mocks({ neutronclient: ('show_bgpvpn_network_assoc',)}) def test_network_association_get(self): bgpvpn = self.bgpvpns.first() na = self.network_associations.first() ret_dict = { 'network_association': self.api_network_associations.first()} self.mock_show_bgpvpn_network_assoc.return_value = ret_dict ret_val = api.bgpvpn.network_association_get( self.request, bgpvpn.id, na.id) self.assertIsInstance(ret_val, api.bgpvpn.NetworkAssociation) @test.create_mocks({ neutronclient: ('list_bgpvpn_network_assocs',)}) def test_network_association_list(self): exp_nas = self.network_associations.list() api_na = {'network_associations': self.api_network_associations.list()} self.mock_list_bgpvpn_network_assocs.return_value = api_na ret_vals = api.bgpvpn.network_association_list(self.request, 'dummy') for (ret_val, exp_na) in zip(ret_vals, exp_nas): self.assertIsInstance(ret_val, api.bgpvpn.NetworkAssociation) self.assertEqual(exp_na.id, ret_val.id) self.assertEqual(exp_na.network_id, ret_val.network_id) @test.create_mocks({ neutronclient: ('create_bgpvpn_network_assoc',)}) def test_network_association_create(self): bgpvpn = self.bgpvpns.first() network = self.networks.first() data = {'network_id': network.id} ret_dict = {'network_association': data} self.mock_create_bgpvpn_network_assoc.return_value = ret_dict ret_val = api.bgpvpn.network_association_create( self.request, bgpvpn.id, **data) self.assertIsInstance(ret_val, api.bgpvpn.NetworkAssociation) self.assertEqual(network.id, ret_val.network_id) @test.create_mocks({ neutronclient: ('delete_bgpvpn_network_assoc',)}) def test_network_association_delete(self): bgpvpn = self.bgpvpns.first() na = self.network_associations.first() api.bgpvpn.network_association_delete(self.request, na.id, bgpvpn.id) self.mock_delete_bgpvpn_network_assoc.assert_called_once_with( bgpvpn.id, na.id) @test.create_mocks({ neutronclient: ('show_bgpvpn_router_assoc',)}) def test_router_association_get(self): bgpvpn = self.bgpvpns.first() na = self.router_associations.first() ret_dict = { 'router_association': self.api_router_associations.first()} self.mock_show_bgpvpn_router_assoc.return_value = ret_dict ret_val = api.bgpvpn.router_association_get( self.request, bgpvpn.id, na.id) self.assertIsInstance(ret_val, api.bgpvpn.RouterAssociation) @test.create_mocks({ neutronclient: ('list_bgpvpn_router_assocs',)}) def test_router_association_list(self): exp_nas = self.router_associations.list() api_na = {'router_associations': self.api_router_associations.list()} self.mock_list_bgpvpn_router_assocs.return_value = api_na ret_vals = api.bgpvpn.router_association_list(self.request, 'dummy') for (ret_val, exp_na) in zip(ret_vals, exp_nas): self.assertIsInstance(ret_val, api.bgpvpn.RouterAssociation) self.assertEqual(exp_na.id, ret_val.id) self.assertEqual(exp_na.router_id, ret_val.router_id) @test.create_mocks({ neutronclient: ('create_bgpvpn_router_assoc',)}) def test_router_association_create(self): bgpvpn = self.bgpvpns.first() router = self.routers.first() data = {'router_id': router.id} ret_dict = {'router_association': data} self.mock_create_bgpvpn_router_assoc.return_value = ret_dict ret_val = api.bgpvpn.router_association_create( self.request, bgpvpn.id, **data) self.assertIsInstance(ret_val, api.bgpvpn.RouterAssociation) self.assertEqual(router.id, ret_val.router_id) @test.create_mocks({ neutronclient: ('delete_bgpvpn_router_assoc',)}) def test_router_association_delete(self): bgpvpn = self.bgpvpns.first() na = self.router_associations.first() api.bgpvpn.router_association_delete(self.request, na.id, bgpvpn.id) self.mock_delete_bgpvpn_router_assoc.assert_called_once_with( bgpvpn.id, na.id) networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/urls.py0000775000175000017500000000125413656750513023136 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-12.0.0/bgpvpn_dashboard/test/helpers.py0000775000175000017500000000201613656750513023610 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 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-12.0.0/bgpvpn_dashboard/test/settings.py0000775000175000017500000000244113656750513024010 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-12.0.0/bgpvpn_dashboard/test/admin/0000775000175000017500000000000013656750625022666 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/admin/__init__.py0000775000175000017500000000000013656750513024764 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/test/admin/test_tables.py0000664000175000017500000000221013656750513025540 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. from django import test import mock from bgpvpn_dashboard.dashboards.admin.bgpvpn import tables as bgpvpn_tables class TestDeleteBgpvpns(test.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-12.0.0/bgpvpn_dashboard/test/admin/test_views.py0000664000175000017500000000617313656750513025437 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-12.0.0/bgpvpn_dashboard/test/admin/test_forms.py0000664000175000017500000001001013656750513025411 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-12.0.0/bgpvpn_dashboard/locale/0000775000175000017500000000000013656750625022056 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/locale/fr/0000775000175000017500000000000013656750625022465 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/locale/fr/LC_MESSAGES/0000775000175000017500000000000013656750625024252 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/locale/fr/LC_MESSAGES/django.po0000664000175000017500000001040713656750513026052 0ustar zuulzuul00000000000000# Cédric Savignan , 2017. #zanata # Thomas Morin , 2017. #zanata # Cédric Savignan , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-12-05 06:33+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-02-12 01:19+0000\n" "Last-Translator: Cédric Savignan \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 4.3.3\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 "Associated Network" msgstr "Réseau Associé" msgid "Associated Networks" msgstr "Réseaux Associés" msgid "Associated Router" msgstr "Router Associé" msgid "Associated Routers" msgstr "Routers Associé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 ID" msgstr "BGPVPN ID" msgid "BGPVPN Interconnections" msgstr "Interconnexions BGPVPN" msgid "BGPVPN Name" msgstr "Nom du 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 "Create a new BGP VPN for any project as you need." msgstr "Créez un nouveau BGPVPN pour chaque projet selon vos besoins." 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 "Description:" msgstr "Description:" msgid "Edit BGPVPN" msgstr "Modifier le 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" msgid "Name" msgstr "Nom" msgid "Network Associations" msgstr "Associations de Réseaux" msgid "Networks" msgstr "Réseaux" msgid "Project" msgstr "Projet" msgid "Project ID" msgstr "Project ID" 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 "Select a project" msgstr "Sélectionner un projet" #, 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 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" #, python-format msgid "Unsupported action type: %s" msgstr "Type d'action non supporté: %s" msgid "Update BGPVPN" msgstr "Modifier BGPVPN" msgid "Update Change" msgstr "Sauvegarger les changements" msgid "You may update the editable properties of your BGP VPN here." msgstr "" "Vous pouvez mettre à jour ici les propriétés modifiables de votre BGP VPN." msgid "l2" msgstr "l2" msgid "l3" msgstr "l3" networking-bgpvpn-12.0.0/bgpvpn_dashboard/locale/en_GB/0000775000175000017500000000000013656750625023030 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013656750625024615 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/bgpvpn_dashboard/locale/en_GB/LC_MESSAGES/django.po0000664000175000017500000001016713656750513026420 0ustar zuulzuul00000000000000# Andi Chandler , 2018. #zanata # Andi Chandler , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-12-05 06:33+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-12-21 02:47+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\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 "" "A single BGP Route Target or a comma-separated list of BGP Route Target. " "Example: 64512:1 or 64512:1,64512:2,64512:3" msgid "Associate Network" msgstr "Associate Network" msgid "Associated Network" msgstr "Associated Network" msgid "Associated Networks" msgstr "Associated Networks" msgid "Associated Router" msgstr "Associated Router" msgid "Associated Routers" msgstr "Associated Routers" msgid "BGP VPNs" msgstr "BGP VPNs" msgid "BGPVPN" msgstr "BGPVPN" #, python-format msgid "BGPVPN %s was successfully created." msgstr "BGPVPN %s was successfully created." #, python-format msgid "BGPVPN %s was successfully updated." msgstr "BGPVPN %s was successfully updated." msgid "BGPVPN ID" msgstr "BGPVPN ID" msgid "BGPVPN Interconnections" msgstr "BGPVPN Interconnections" msgid "BGPVPN Name" msgstr "BGPVPN Name" msgid "" "Bug, inconsistency between neutron-lib and networking-bgpvpn for RTRD regex" msgstr "" "Bug, inconsistency between neutron-lib and networking-bgpvpn for RTRD regex" msgid "Create BGPVPN" msgstr "Create BGPVPN" msgid "Create a new BGP VPN for any project as you need." msgstr "Create a new BGP VPN for any project as you need." msgid "Delete BGPVPN" msgid_plural "Delete BGPVPNs" msgstr[0] "Delete BGPVPN" msgstr[1] "Delete BGPVPNs" msgid "Deleted BGPVPN" msgid_plural "Deleted BGPVPNs" msgstr[0] "Deleted BGPVPN" msgstr[1] "Deleted BGPVPNs" msgid "Description:" msgstr "Description:" msgid "Edit BGPVPN" msgstr "Edit BGPVPN" msgid "Export Targets" msgstr "Export Targets" msgid "Export targets" msgstr "Export targets" msgid "Export targets is not valid" msgstr "Export targets is not valid" #, python-format msgid "Failed to create BGPVPN %s" msgstr "Failed to create BGPVPN %s" #, python-format msgid "Failed to delete BGPVPN %s" msgstr "Failed to delete BGPVPN %s" #, python-format msgid "Failed to update BGPVPN %s" msgstr "Failed to update 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 is not valid" msgid "Name" msgstr "Name" msgid "Network Associations" msgstr "Network Associations" msgid "Networks" msgstr "Networks" msgid "Project" msgstr "Project" msgid "Project ID" msgstr "Project ID" msgid "Route Targets" msgstr "Route Targets" msgid "Route targets" msgstr "Route targets" msgid "Route targets is not valid" msgstr "Route targets is not valid" msgid "Router Associations" msgstr "Router Associations" msgid "Routers" msgstr "Routers" msgid "Select a project" msgstr "Select a project" #, python-format msgid "Something went wrong with BGPVPN %s" msgstr "Something went wrong with BGPVPN %s" msgid "The type of VPN and the technology behind it." msgstr "The type of VPN and the technology behind it." msgid "Type" msgstr "Type" msgid "Unable to retrieve BGPVPN details." msgstr "Unable to retrieve BGPVPN details." #, python-format msgid "Unable to retrieve information about the tenant %s" msgstr "Unable to retrieve information about the tenant %s" #, python-format msgid "Unsupported action type: %s" msgstr "Unsupported action type: %s" msgid "Update BGPVPN" msgstr "Update BGPVPN" msgid "Update Change" msgstr "Update Change" msgid "You may update the editable properties of your BGP VPN here." msgstr "You may update the editable properties of your BGP VPN here." msgid "l2" msgstr "l2" msgid "l3" msgstr "l3" networking-bgpvpn-12.0.0/networking_bgpvpn/0000775000175000017500000000000013656750625021057 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/__init__.py0000664000175000017500000000121013656750513023156 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-12.0.0/networking_bgpvpn/version.py0000664000175000017500000000127013656750513023112 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-12.0.0/networking_bgpvpn/policies/0000775000175000017500000000000013656750625022666 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/policies/__init__.py0000664000175000017500000000176713656750513025006 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools from networking_bgpvpn.policies import bgpvpn from networking_bgpvpn.policies import network_association from networking_bgpvpn.policies import port_association from networking_bgpvpn.policies import router_association def list_rules(): return itertools.chain( bgpvpn.list_rules(), network_association.list_rules(), router_association.list_rules(), port_association.list_rules(), ) networking-bgpvpn-12.0.0/networking_bgpvpn/policies/network_association.py0000664000175000017500000000546313656750513027331 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from networking_bgpvpn.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgpvpn_network_association', base.RULE_ADMIN_OR_OWNER, 'Create a network association', [ { 'method': 'POST', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/network_associations', }, ] ), # TODO(amotoki): PUT operation is not defined in the API ref. Drop it? policy.DocumentedRuleDefault( 'update_bgpvpn_network_association', base.RULE_ADMIN_OR_OWNER, 'Update a network association', [ { 'method': 'PUT', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'network_associations/{network_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'delete_bgpvpn_network_association', base.RULE_ADMIN_OR_OWNER, 'Delete a network association', [ { 'method': 'DELETE', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'network_associations/{network_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn_network_association', base.RULE_ADMIN_OR_OWNER, 'Get network associations', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/network_associations', }, { 'method': 'GET', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'network_associations/{network_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn_network_association:tenant_id', base.RULE_ADMIN_ONLY, 'Get ``tenant_id`` attributes of network associations', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/network_associations', }, { 'method': 'GET', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'network_associations/{network_association_id}'), }, ] ), ] def list_rules(): return rules networking-bgpvpn-12.0.0/networking_bgpvpn/policies/port_association.py0000664000175000017500000000525113656750513026617 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from networking_bgpvpn.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgpvpn_port_association', base.RULE_ADMIN_OR_OWNER, 'Create a port association', [ { 'method': 'POST', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/port_associations', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn_port_association', base.RULE_ADMIN_OR_OWNER, 'Update a port association', [ { 'method': 'PUT', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'port_associations/{port_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'delete_bgpvpn_port_association', base.RULE_ADMIN_OR_OWNER, 'Delete a port association', [ { 'method': 'DELETE', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'port_associations/{port_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn_port_association', base.RULE_ADMIN_OR_OWNER, 'Get port associations', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/port_associations', }, { 'method': 'GET', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'port_associations/{port_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn_port_association:tenant_id', base.RULE_ADMIN_ONLY, 'Get ``tenant_id`` attributes of port associations', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/port_associations', }, { 'method': 'GET', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'port_associations/{port_association_id}'), }, ] ), ] def list_rules(): return rules networking-bgpvpn-12.0.0/networking_bgpvpn/policies/bgpvpn.py0000664000175000017500000001375313656750513024541 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from networking_bgpvpn.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgpvpn', base.RULE_ADMIN_ONLY, 'Create a BGP VPN', [ { 'method': 'POST', 'path': '/bgpvpn/bgpvpns', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn', base.RULE_ADMIN_OR_OWNER, 'Update a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), # TODO(amotoki): tenant_id is not updatable, so perhaps this can be dropped policy.DocumentedRuleDefault( 'update_bgpvpn:tenant_id', base.RULE_ADMIN_ONLY, 'Update ``tenant_id`` attribute of a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn:route_targets', base.RULE_ADMIN_ONLY, 'Update ``route_targets`` attribute of a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn:import_targets', base.RULE_ADMIN_ONLY, 'Update ``import_targets`` attribute of a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn:export_targets', base.RULE_ADMIN_ONLY, 'Update ``export_targets`` attribute of a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn:route_distinguishers', base.RULE_ADMIN_ONLY, 'Update ``route_distinguishers`` attribute of a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), # TODO(amotoki): vni is not updatable, so perhaps this can be dropped policy.DocumentedRuleDefault( 'update_bgpvpn:vni', base.RULE_ADMIN_ONLY, 'Update ``vni`` attribute of a BGP VPN', [ { 'method': 'PUT', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_bgpvpn', base.RULE_ADMIN_ONLY, 'Delete a BGP VPN', [ { 'method': 'DELETE', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn', base.RULE_ADMIN_OR_OWNER, 'Get BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn:tenant_id', base.RULE_ADMIN_ONLY, 'Get ``tenant_id`` attributes of BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn:route_targets', base.RULE_ADMIN_ONLY, 'Get ``route_targets`` attributes of BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn:import_targets', base.RULE_ADMIN_ONLY, 'Get ``import_targets`` attributes of BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn:export_targets', base.RULE_ADMIN_ONLY, 'Get ``export_targets`` attributes of BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn:route_distinguishers', base.RULE_ADMIN_ONLY, 'Get ``route_distinguishers`` attributes of BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn:vni', base.RULE_ADMIN_ONLY, 'Get ``vni`` attributes of BGP VPNs', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns', }, { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{id}', }, ] ), ] def list_rules(): return rules networking-bgpvpn-12.0.0/networking_bgpvpn/policies/router_association.py0000664000175000017500000000532313656750513027153 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from networking_bgpvpn.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgpvpn_router_association', base.RULE_ADMIN_OR_OWNER, 'Create a router association', [ { 'method': 'POST', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/router_associations', }, ] ), policy.DocumentedRuleDefault( 'update_bgpvpn_router_association', base.RULE_ADMIN_OR_OWNER, 'Update a router association', [ { 'method': 'PUT', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'router_associations/{router_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'delete_bgpvpn_router_association', base.RULE_ADMIN_OR_OWNER, 'Delete a router association', [ { 'method': 'DELETE', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'router_associations/{router_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn_router_association', base.RULE_ADMIN_OR_OWNER, 'Get router associations', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/router_associations', }, { 'method': 'GET', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'router_associations/{router_association_id}'), }, ] ), policy.DocumentedRuleDefault( 'get_bgpvpn_router_association:tenant_id', base.RULE_ADMIN_ONLY, 'Get ``tenant_id`` attributes of router associations', [ { 'method': 'GET', 'path': '/bgpvpn/bgpvpns/{bgpvpn_id}/router_associations', }, { 'method': 'GET', 'path': ('/bgpvpn/bgpvpns/{bgpvpn_id}/' 'router_associations/{router_association_id}'), }, ] ), ] def list_rules(): return rules networking-bgpvpn-12.0.0/networking_bgpvpn/policies/base.py0000664000175000017500000000132313656750513024145 0ustar zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # TODO(amotoki): Define these in neutron or neutron-lib RULE_ADMIN_OR_OWNER = 'rule:admin_or_owner' RULE_ADMIN_ONLY = 'rule:admin_only' RULE_ANY = 'rule:regular_user' networking-bgpvpn-12.0.0/networking_bgpvpn/tests/0000775000175000017500000000000013656750625022221 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/__init__.py0000664000175000017500000000000013656750513024314 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/0000775000175000017500000000000013656750625023200 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/__init__.py0000664000175000017500000000000013656750513025273 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/client/0000775000175000017500000000000013656750625024456 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/client/__init__.py0000664000175000017500000000000013656750513026551 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/client/test_client.py0000664000175000017500000000477613656750513027357 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-12.0.0/networking_bgpvpn/tests/unit/services/0000775000175000017500000000000013656750625025023 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/services/__init__.py0000664000175000017500000000000013656750513027116 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/services/common/0000775000175000017500000000000013656750625026313 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/services/common/__init__.py0000664000175000017500000000000013656750513030406 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/services/common/test_utils.py0000664000175000017500000000446313656750513031067 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-12.0.0/networking_bgpvpn/tests/unit/services/bagpipe/0000775000175000017500000000000013656750625026432 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/services/bagpipe/__init__.py0000664000175000017500000000000013656750513030525 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/services/bagpipe/test_bagpipe.py0000664000175000017500000013703313656750513031455 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_lib.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-12.0.0/networking_bgpvpn/tests/unit/services/test_plugin.py0000664000175000017500000017711513656750513027742 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.ALIAS + ':dummy:networking_bgpvpn.neutron.services.' 'service_drivers.driver_api.BGPVPNDriverRC:default') else: provider = (bgpvpn_def.ALIAS + ':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.ALIAS: 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.ALIAS) 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)) def test_bgpvpn_port_assoc_update_bgpvpn_route_wrong_type(self): with self.network() as net, \ self.subnet(network={'network': net['network']}) as subnet, \ self.port(subnet={'subnet': subnet['subnet']}) as port, \ self.bgpvpn(type='l2') as bgpvpn_l2, \ self.bgpvpn(type='l3') as bgpvpn_l3, \ self.assoc_port(bgpvpn_l2['bgpvpn']['id'], port['port']['id']) as port_assoc: bgpvpn_id = bgpvpn_l2['bgpvpn']['id'] req = self.new_update_request( 'bgpvpn/bgpvpns/%s/port_associations' % bgpvpn_id, {'port_association': { 'routes': [{ 'type': 'bgpvpn', 'bgpvpn_id': bgpvpn_l3['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("differing from type of associated BGPVPN", 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-12.0.0/networking_bgpvpn/tests/unit/extensions/0000775000175000017500000000000013656750625025377 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/extensions/__init__.py0000664000175000017500000000000013656750513027472 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn_routes_control.py0000664000175000017500000002621513656750513033447 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 neutron_lib.utils import test 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.ALIAS, [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: {}} ] @test.unstable_test("bug/1791256") 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-12.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn_rc_base.py0000664000175000017500000001055413656750513031763 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 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) #################################################################### self.ext_mdw = test_extensions.setup_extensions_middleware(ext_mgr) self.api = webtest.TestApp(self.ext_mdw) networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn.py0000664000175000017500000003743013656750513030307 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.api import extensions 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 import extensions as bgpvpn_extensions 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): # NOTE(tmorin): this is already done in # networking_bgpvpn.neutron.extensions.bgpvpn at module loading time, # but for some reason I don't understand this is overridden later, # which is why we re-force this here: extensions.append_api_extensions_path(bgpvpn_extensions.__path__) super(BgpvpnExtensionTestCaseBase, self).setUp() plural_mappings = {'bgpvpn': 'bgpvpns'} self.setup_extension( '%s.%s' % (bgpvpn.BGPVPNPluginBase.__module__, bgpvpn.BGPVPNPluginBase.__name__), bgpvpn_api_def.ALIAS, 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-12.0.0/networking_bgpvpn/tests/unit/extensions/test_bgpvpn_vni.py0000664000175000017500000001045013656750513031154 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.setup_extension( BGPVPN_PLUGIN_BASE_NAME, bgpvpn_def.ALIAS, 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-12.0.0/networking_bgpvpn/tests/unit/db/0000775000175000017500000000000013656750625023565 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/db/__init__.py0000664000175000017500000000000013656750513025660 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/unit/db/test_db.py0000664000175000017500000005231513656750513025565 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-12.0.0/networking_bgpvpn/tests/functional/0000775000175000017500000000000013656750625024363 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/functional/__init__.py0000664000175000017500000000000013656750513026456 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/functional/requirements.txt0000664000175000017500000000040213656750513027637 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-12.0.0/networking_bgpvpn/tests/functional/db/0000775000175000017500000000000013656750625024750 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/functional/db/__init__.py0000664000175000017500000000000013656750513027043 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/tests/functional/db/test_migrations.py0000664000175000017500000000504113656750513030531 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_', ) # 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-12.0.0/networking_bgpvpn/tests/functional/test_placeholder.py0000664000175000017500000000016213656750513030251 0ustar zuulzuul00000000000000from neutron.tests import base class PlaceholderTest(base.BaseTestCase): def test_noop(self): pass networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/0000775000175000017500000000000013656750625022551 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/__init__.py0000664000175000017500000000000013656750513024644 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/0000775000175000017500000000000013656750625024374 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/__init__.py0000664000175000017500000000000013656750513026467 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/common/0000775000175000017500000000000013656750625025664 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/common/utils.py0000664000175000017500000001035313656750513027374 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.ALIAS) 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-12.0.0/networking_bgpvpn/neutron/services/common/__init__.py0000664000175000017500000000000013656750513027757 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/common/constants.py0000775000175000017500000000173013656750513030252 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-12.0.0/networking_bgpvpn/neutron/services/service_drivers/0000775000175000017500000000000013656750625027572 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/service_drivers/__init__.py0000664000175000017500000000000013656750513031665 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/service_drivers/driver_api.py0000664000175000017500000003766413656750513032304 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_lib.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, context, 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_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_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_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_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_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_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_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()] @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_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_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_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_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-12.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/0000775000175000017500000000000013656750625031201 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/__init__.py0000664000175000017500000000000013656750513033274 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/bagpipe.py0000664000175000017500000005132013656750513033157 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.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 neutron_lib.db import api as db_api 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_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, models_v2.IPAllocation.port_id == models_v2.Port.id). 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_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_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_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_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_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_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_READER def get_networks_for_router(context, router_id): ports = get_router_ports(context, router_id) if ports: return {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}) @registry.has_registry_receivers 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() 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_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) @registry.receives(resources.PORT, [events.AFTER_UPDATE]) @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) @registry.receives(resources.PORT, [events.AFTER_DELETE]) @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) # contrary to mother class, no need to subscribe to router interface # before-delete, because after delete, we still can generate RPCs @registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_DELETE]) @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-12.0.0/networking_bgpvpn/neutron/services/service_drivers/bagpipe/bagpipe_v2.py0000664000175000017500000002407213656750513033572 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.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.db import api as db_api from neutron_lib import exceptions as n_exc from neutron_lib.plugins import directory from networking_bagpipe.objects import bgpvpn as bgpvpn_objects 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 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_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}) @registry.has_registry_receivers 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) 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) @registry.receives(resources.ROUTER_INTERFACE, [events.AFTER_CREATE]) @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) # need to subscribe to router interface *before*_delete # because after delete, we can't build the OVO objects from the DB anymore @registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_DELETE]) @log_helpers.log_method_call def registry_router_interface_deleted(self, resource, event, trigger, payload=None): try: context = payload.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 = payload.resource_id subnet_id = payload.metadata['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, {'subnet_id': subnet_id, 'router_id': router_id}, e) networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/services/plugin.py0000664000175000017500000003513113656750513026243 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__) # ("BGPVPN" is the string to match as the first part of the # service_provider configuration: "BGPVPN:Dummy:networking_bgpvpn ...") SERVICE_PROVIDER_TYPE = "BGPVPN" @registry.has_registry_receivers 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( SERVICE_PROVIDER_TYPE, pconf.ProviderConfiguration('networking_bgpvpn')) # Load the default driver drivers, default_provider = service_base.load_drivers( SERVICE_PROVIDER_TYPE, 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") @property def supported_extension_aliases(self): exts = copy.copy(super(BGPVPNPlugin, self).supported_extension_aliases) exts += self.driver.more_supported_extension_aliases return exts @registry.receives(resources.ROUTER_INTERFACE, [events.BEFORE_CREATE]) 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.ALIAS 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_bgpvpn = self.get_bgpvpn(context, bgpvpn_id) if route_bgpvpn['type'] != assoc_bgpvpn['type']: raise bgpvpn_rc.BGPVPNPortAssocRouteBGPVPNTypeDiffer( route_bgpvpn_type=route_bgpvpn['type'], bgpvpn_type=assoc_bgpvpn['type'] ) 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-12.0.0/networking_bgpvpn/neutron/opts.py0000664000175000017500000000220413656750513024102 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 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]) networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/extensions/0000775000175000017500000000000013656750625024750 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/extensions/__init__.py0000664000175000017500000000000013656750513027043 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/extensions/bgpvpn.py0000664000175000017500000001577213656750513026626 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.ALIAS, register_quota=True, translate_name=True) plugin = directory.get_plugin(bgpvpn_api_def.ALIAS) 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 @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_type(self): return bgpvpn_api_def.ALIAS 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-12.0.0/networking_bgpvpn/neutron/extensions/bgpvpn_vni.py0000664000175000017500000000206613656750513027472 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-12.0.0/networking_bgpvpn/neutron/extensions/bgpvpn_routes_control.py0000664000175000017500000001057513656750513031763 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 BGPVPNPortAssocRouteBGPVPNTypeDiffer(n_exc.BadRequest): message = _("bgpvpn specified in route is of type %(route_bgpvpn_type)s, " "differing from type of associated BGPVPN %(bgpvpn_type)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.ALIAS) # 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] @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-12.0.0/networking_bgpvpn/neutron/db/0000775000175000017500000000000013656750625023136 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/head.py0000664000175000017500000000134513656750513024410 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-12.0.0/networking_bgpvpn/neutron/db/__init__.py0000664000175000017500000000000013656750513025231 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/0000775000175000017500000000000013656750625025127 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/__init__.py0000664000175000017500000000000013656750513027222 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/0000775000175000017500000000000013656750625030757 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/__init__.py0000664000175000017500000000000013656750513033052 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/0000775000175000017500000000000013656750625032627 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/0000775000175000017500000000000013656750625034141 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/expand/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/e0000775000175000017500000000000013656750625034306 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022300000000000011212 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/expand/0ab4049986b8_add_indexes_to_tenant_id.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/e0000664000175000017500000000203613656750513034305 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) ././@LongLink0000000000000000000000000000015500000000000011216 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/contract/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/c0000775000175000017500000000000013656750625034304 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022500000000000011214 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/contract/23ce05e0a19f_rename_tenant_to_project.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/newton/c0000664000175000017500000000622013656750513034302 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() } ././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/CONTRACT_HEADnetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/CONTRACT0000664000175000017500000000001513656750513033757 0ustar zuulzuul000000000000009d7f1ae5fa56 networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/0000775000175000017500000000000013656750625033756 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015200000000000011213 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/expand/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/ex0000775000175000017500000000000013656750625034313 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022100000000000011210 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/expand/7a9482036ecd_add_standard_attributes.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/ex0000664000175000017500000000221713656750513034313 0ustar zuulzuul00000000000000# Copyright 2018 OpenStack Fundation # # 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 standard attributes Revision ID: 7a9482036ecd Revises: 666c706fea3b Create Date: 2018-04-04 10:12:40.399032 """ # revision identifiers, used by Alembic. revision = '7a9482036ecd' down_revision = '666c706fea3b' from alembic import op import sqlalchemy as sa def upgrade(): for table in ('bgpvpns', 'bgpvpn_network_associations', 'bgpvpn_router_associations', 'bgpvpn_port_associations'): op.add_column(table, sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) ././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/contract/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/co0000775000175000017500000000000013656750625034300 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022300000000000011212 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/contract/9d7f1ae5fa56_add_standard_attributes.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/co0000664000175000017500000000617113656750513034303 0ustar zuulzuul00000000000000# Copyright 2018 OpenStack Fundation # # 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 standard FK and constraints, and defs for existing objects Revision ID: 9d7f1ae5fa56 Revises: 23ce05e0a19f Create Date: 2018-04-19 12:44:54.352253 """ # revision identifiers, used by Alembic. revision = '9d7f1ae5fa56' down_revision = '23ce05e0a19f' depends_on = ('7a9482036ecd',) from alembic import op import sqlalchemy as sa # adapted from b12a3ef66e62_add_standardattr_to_qos_policies.py standardattrs = sa.Table( 'standardattributes', sa.MetaData(), sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True), sa.Column('resource_type', sa.String(length=255), nullable=False), sa.Column('description', sa.String(length=255), nullable=True)) def upgrade(): for table in ('bgpvpns', 'bgpvpn_network_associations', 'bgpvpn_router_associations', 'bgpvpn_port_associations'): upgrade_table(table) def upgrade_table(table): table_model = sa.Table( table, sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) generate_records_for_existing(table, table_model) # add the constraint now that everything is populated on that table op.alter_column(table, 'standard_attr_id', nullable=False, existing_type=sa.BigInteger(), existing_nullable=True, existing_server_default=False) op.create_unique_constraint( constraint_name='uniq_%s0standard_attr_id' % table, table_name=table, columns=['standard_attr_id']) op.create_foreign_key( constraint_name=None, source_table=table, referent_table='standardattributes', local_cols=['standard_attr_id'], remote_cols=['id'], ondelete='CASCADE') def generate_records_for_existing(table, table_model): session = sa.orm.Session(bind=op.get_bind()) values = [] with session.begin(subtransactions=True): for row in session.query(table_model): # NOTE(kevinbenton): without this disabled, pylint complains # about a missing 'dml' argument. # pylint: disable=no-value-for-parameter res = session.execute( standardattrs.insert().values(resource_type=table) ) session.execute( table_model.update().values( standard_attr_id=res.inserted_primary_key[0]).where( table_model.c.id == row[0]) ) # this commit is necessary to allow further operations session.commit() return values ././@LongLink0000000000000000000000000000015000000000000011211 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/EXPAND_HEADnetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/EXPAND_H0000664000175000017500000000001513656750513033770 0ustar zuulzuul000000000000007a9482036ecd networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000775000175000017500000000000013656750625034301 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015400000000000011215 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000775000175000017500000000000013656750625034301 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000023000000000000011210 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/3600132c6147_add_router_association_table.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000664000175000017500000000301513656750513034276 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') ) ././@LongLink0000000000000000000000000000020300000000000011210 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/expand/17d9fd4fddee_initial.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000664000175000017500000000435613656750513034307 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') ) ././@LongLink0000000000000000000000000000015600000000000011217 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/contract/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000775000175000017500000000000013656750625034301 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000020500000000000011212 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/contract/180baa4183e0_initial.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/liberty/0000664000175000017500000000165113656750513034302 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 ././@LongLink0000000000000000000000000000016700000000000011221 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/start_networking_bgpvpn.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/start_ne0000664000175000017500000000154513656750513034372 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-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/0000775000175000017500000000000013656750625034127 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000015300000000000011214 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/e0000775000175000017500000000000013656750625034274 5ustar zuulzuul00000000000000././@LongLink0000000000000000000000000000022200000000000011211 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/39411aacf9b8_add_vni_to_bgpvpn_table.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/e0000664000175000017500000000171613656750513034277 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)) ././@LongLink0000000000000000000000000000022500000000000011214 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/9a6664f3b8d4_add_port_association_table.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/e0000664000175000017500000000601213656750513034271 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. # """Add tables for port associations 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 ) ././@LongLink0000000000000000000000000000021400000000000011212 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/666c706fea3b_bgpvpn_local_pref.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/e0000664000175000017500000000206713656750513034277 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)) ././@LongLink0000000000000000000000000000024200000000000011213 Lustar 00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/expand/4610803bdf0d_router_assoc_add_advertise_extra_routes.pynetworking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/queens/e0000664000175000017500000000217213656750513034274 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. # """Add 'extra-routes' to router association table 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-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/README0000664000175000017500000000004613656750513031633 0ustar zuulzuul00000000000000Generic single-database configuration.networking-bgpvpn-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/env.py0000664000175000017500000000466513656750513032130 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-12.0.0/networking_bgpvpn/neutron/db/migration/alembic_migrations/script.py.mako0000664000175000017500000000203713656750513033561 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-12.0.0/networking_bgpvpn/neutron/db/bgpvpn_db.py0000664000175000017500000006167713656750513025466 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_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 api as db_api from neutron_lib.db import constants as db_const from neutron_lib.db import model_base from neutron_lib.db import model_query from neutron_lib.db import standard_attr from neutron_lib.db import utils as db_utils 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(standard_attr.HasStandardAttributes, 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',) # standard attributes support: api_collections = [] api_sub_resources = [bgpvpn_def.NETWORK_ASSOCIATIONS] collection_resource_map = {bgpvpn_def.NETWORK_ASSOCIATIONS: bgpvpn_def.NETWORK_ASSOCIATION} class BGPVPNRouterAssociation(standard_attr.HasStandardAttributes, 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',) # standard attributes support: api_collections = [] api_sub_resources = [bgpvpn_def.ROUTER_ASSOCIATIONS] collection_resource_map = {bgpvpn_def.ROUTER_ASSOCIATIONS: bgpvpn_def.ROUTER_ASSOCIATION} class BGPVPNPortAssociation(standard_attr.HasStandardAttributes, 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',) # standard attributes support: api_collections = [] api_sub_resources = [bgpvpn_rc_def.PORT_ASSOCIATIONS] collection_resource_map = {bgpvpn_rc_def.PORT_ASSOCIATIONS: bgpvpn_rc_def.PORT_ASSOCIATION} class BGPVPN(standard_attr.HasStandardAttributes, 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="vpn_types"), 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') # standard attributes support: api_collections = [bgpvpn_def.COLLECTION_NAME] collection_resource_map = {bgpvpn_def.COLLECTION_NAME: bgpvpn_def.RESOURCE_NAME} 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_association_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('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') 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_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(object): """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_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_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.ALIAS) 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 db_utils.resource_fields(res, fields) @db_api.CONTEXT_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_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_READER def get_bgpvpns(self, context, filters=None, fields=None): return model_query.get_collection( context, BGPVPN, self._make_bgpvpn_dict, filters=filters, fields=fields) @db_api.CONTEXT_READER def _get_bgpvpn(self, context, id): try: return model_query.get_by_id(context, BGPVPN, id) except exc.NoResultFound: raise bgpvpn_ext.BGPVPNNotFound(id=id) @db_api.CONTEXT_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_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_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_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 db_utils.resource_fields(res, fields) @db_api.CONTEXT_READER def _get_net_assoc(self, context, assoc_id, bgpvpn_id): try: query = model_query.query_with_hooks(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_WRITER def create_net_assoc(self, context, bgpvpn_id, net_assoc): try: with db_api.CONTEXT_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_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_READER def get_net_assocs(self, context, bgpvpn_id, filters=None, fields=None): if not filters: filters = {} filters['bgpvpn_id'] = [bgpvpn_id] return model_query.get_collection( context, BGPVPNNetAssociation, self._make_net_assoc_dict, filters, fields) @db_api.CONTEXT_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_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 db_utils.resource_fields(res, fields) @db_api.CONTEXT_READER def _get_router_assoc(self, context, assoc_id, bgpvpn_id): try: query = model_query.query_with_hooks( 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_WRITER def create_router_assoc(self, context, bgpvpn_id, router_association): router_id = router_association['router_id'] try: with db_api.CONTEXT_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_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_READER def get_router_assocs(self, context, bgpvpn_id, filters=None, fields=None): if not filters: filters = {} filters['bgpvpn_id'] = [bgpvpn_id] return model_query.get_collection( context, BGPVPNRouterAssociation, self._make_router_assoc_dict, filters, fields) @db_api.CONTEXT_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_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_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 db_utils.resource_fields(res, fields) @db_api.CONTEXT_READER def _get_port_assoc(self, context, assoc_id, bgpvpn_id): try: query = model_query.query_with_hooks( 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_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_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_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_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_READER def get_port_assocs(self, context, bgpvpn_id, filters=None, fields=None): if not filters: filters = {} filters['bgpvpn_id'] = [bgpvpn_id] return model_query.get_collection( context, BGPVPNPortAssociation, self._make_port_assoc_dict, filters, fields) @db_api.CONTEXT_READER def update_port_assoc(self, context, assoc_id, bgpvpn_id, port_assoc): with db_api.CONTEXT_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_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-12.0.0/networking_bgpvpn/locale/0000775000175000017500000000000013656750625022316 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/locale/en_GB/0000775000175000017500000000000013656750625023270 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013656750625025055 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/locale/en_GB/LC_MESSAGES/networking_bgpvpn.po0000664000175000017500000001305613656750513031161 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata # Andi Chandler , 2018. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-12-05 06:33+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2018-02-09 11:27+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #, python-format msgid "%(method)s failed." msgstr "%(method)s failed." 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" #, python-format msgid "BGPVPN %(driver)s driver does not support %(type)s type" msgstr "BGPVPN %(driver)s driver does not support %(type)s type" #, python-format msgid "" "BGPVPN %(driver)s driver does not support multiple router association with a " "bgpvpn" msgstr "" "BGPVPN %(driver)s driver does not support multiple router association with a " "BGPVPN" #, python-format msgid "BGPVPN %(driver)s driver does not support router associations" msgstr "BGPVPN %(driver)s driver does not support router associations" #, python-format msgid "" "BGPVPN %(driver)s driver does not support to fetch BGPVPNs associated to " "network id %(net_id)" msgstr "" "BGPVPN %(driver)s driver does not support to fetch BGPVPNs associated to " "network id %(net_id)" #, python-format msgid "" "BGPVPN %(driver)s driver does not support to manually set route distinguisher" msgstr "" "BGPVPN %(driver)s driver does not support to manually set route distinguisher" #, python-format msgid "BGPVPN %(id)s could not be found" msgstr "BGPVPN %(id)s could not be found" #, python-format msgid "" "BGPVPN network association %(id)s could not be found for BGPVPN %(bgpvpn_id)s" msgstr "" "BGPVPN network association %(id)s could not be found for BGPVPN %(bgpvpn_id)s" #, python-format msgid "" "BGPVPN port association %(id)s could not be found for BGPVPN %(bgpvpn_id)s" msgstr "" "BGPVPN port association %(id)s could not be found for BGPVPN %(bgpvpn_id)s" #, python-format msgid "" "BGPVPN router association %(id)s could not be found for BGPVPN %(bgpvpn_id)s" msgstr "" "BGPVPN router association %(id)s could not be found for BGPVPN %(bgpvpn_id)s" msgid "ID or name of the BGPVPN." msgstr "ID or name of the BGPVPN." msgid "ID or name of the network." msgstr "ID or name of the network." msgid "ID or name of the router." msgstr "ID or name of the router." msgid "" "It is not allowed to add an interface to a router if both the router and the " "network are bound to an L3 BGPVPN." msgstr "" "It is not allowed to add an interface to a router if both the router and the " "network are bound to an L3 BGPVPN." msgid "" "List of RDs that will be used to advertize VPN routes.Usage: -- --route-" "distinguishers list=true : : ..." msgstr "" "List of RDs that will be used to advertise VPN routes.Usage: -- --route-" "distinguishers list=true : : ..." msgid "" "List of additional Route Targets to export to. Usage: -- --export-targets " "list=true : : ..." msgstr "" "List of additional Route Targets to export to. Usage: -- --export-targets " "list=true : : ..." msgid "" "List of additional Route Targets to import from. Usage: -- --import-targets " "list=true : : ..." msgstr "" "List of additional Route Targets to import from. Usage: -- --import-targets " "list=true : : ..." msgid "Name of the BGP VPN" msgstr "Name of the BGP VPN" #, python-format msgid "" "Network %(network)s already associated with %(bgpvpn)s. BGPVPN %(driver)s " "driver does not support same network associated to multiple bgpvpns" msgstr "" "Network %(network)s already associated with %(bgpvpn)s. BGPVPN %(driver)s " "driver does not support same network associated to multiple BGPVPNs" msgid "" "Route Targets list to import/export for this BGP VPN. Usage: -- --route-" "targets list=true : : ..." msgstr "" "Route Targets list to import/export for this BGP VPN. Usage: -- --route-" "targets list=true : : ..." #, python-format msgid "bgpvpn specified in route does not belong to the tenant (%(bgpvpn_id)s)" msgstr "" "BGPVPN specified in route does not belong to the tenant (%(bgpvpn_id)s)" #, python-format msgid "bgpvpn specified in route does not exist (%(bgpvpn_id)s)" msgstr "BGPVPN specified in route does not exist (%(bgpvpn_id)s)" #, python-format msgid "" "bgpvpn specified in route is of type %(route_bgpvpn_type)s, differing from " "type of associated BGPVPN %(bgpvpn_type)s)" msgstr "" "BGPVPN specified in route is of type %(route_bgpvpn_type)s, differing from " "type of associated BGPVPN %(bgpvpn_type)s)" msgid "driver does not support associating an externalnetwork to a BGPVPN" msgstr "driver does not support associating an external network to a BGPVPN" #, python-format msgid "network %(net_id)s is already associated to BGPVPN %(bgpvpn_id)s" msgstr "network %(net_id)s is already associated to BGPVPN %(bgpvpn_id)s" #, python-format msgid "port %(port_id)s is already associated to BGPVPN %(bgpvpn_id)s" msgstr "port %(port_id)s is already associated to BGPVPN %(bgpvpn_id)s" #, python-format msgid "router %(router_id)s is already associated to BGPVPN %(bgpvpn_id)s" msgstr "router %(router_id)s is already associated to BGPVPN %(bgpvpn_id)s" networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/0000775000175000017500000000000013656750625023750 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/__init__.py0000664000175000017500000000000013656750513026043 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/0000775000175000017500000000000013656750625025442 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/__init__.py0000664000175000017500000000000013656750513027535 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/0000775000175000017500000000000013656750625026210 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/__init__.py0000664000175000017500000000000013656750513030303 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/0000775000175000017500000000000013656750625027504 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/__init__.py0000664000175000017500000000000013656750513031577 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn/neutronclient/neutron/v2_0/bgpvpn/bgpvpn.py0000775000175000017500000002213013656750513031347 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-12.0.0/networking_bgpvpn/_i18n.py0000664000175000017500000000256613656750513022354 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-12.0.0/tox.ini0000664000175000017500000000765613656750513016641 0ustar zuulzuul00000000000000[tox] minversion = 3.1.1 envlist = py37,pep8 skipsdist = True ignore_basepython_conflict = True [testenv] usedevelop = True basepython = python3 install_command = pip install {opts} {packages} setenv = VIRTUAL_ENV={envdir} OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true} OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true} OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true} PYTHONWARNINGS=default::DeprecationWarning deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = stestr run {posargs} python {toxinidir}/tools/django-manage.py test bgpvpn_dashboard [testenv:releasenotes] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt 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:genpolicy]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 = stestr run {posargs} [testenv:dsvm-functional] setenv = {[testenv:functional]setenv} {[testenv:dsvm]setenv} sitepackages=True deps = {[testenv:functional]deps} commands = stestr run {posargs} [testenv:venv] commands = {posargs} [testenv:cover] setenv = PYTHON = coverage run --source networking_bgpvpn --parallel-mode commands = stestr run {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:docs] deps = -c{env:UPPER_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt commands = sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] envdir = {toxworkdir}/docs deps = {[testenv:docs]deps} whitelist_externals = make commands = sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [testenv:debug] commands = oslo_debug_helper -t networking_bgpvpn/tests/unit {posargs} [testenv:genconfig] commands = {toxinidir}/tools/generate_config_file_samples.sh [testenv:genpolicy] commands = oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf [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 [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt [testenv:dev] # run locally (not in the gate) using editable mode # https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs # note that order is important to ensure dependencies don't override commands = pip install -q -e "git+https://opendev.org/openstack/networking-bagpipe#egg=networking_bagpipe" pip install -q -e "git+https://opendev.org/openstack/neutron#egg=neutron" [testenv:py3-dev] commands = {[testenv:dev]commands} {[testenv]commands} [testenv:pep8-dev] deps = {[testenv:pep8]deps} commands = {[testenv:dev]commands} {[testenv:pep8]commands} networking-bgpvpn-12.0.0/ChangeLog0000664000175000017500000006132313656750624017072 0ustar zuulzuul00000000000000CHANGES ======= 12.0.0 ------ * Fix recent gate failures * Add release note on horizon optional dependency * Use extras for horizon dependency * Switch functional/install jobs to Zuulv3 syntax 12.0.0.0b1 ---------- * Remove references for unittest2 * Fix lower constraints * Fix sphinx requirements * Drop Django 1.11 support * translation: drop babel extractor definitions * Imported Translations from Zanata * [ussuri][goal] Drop python 2.7 support and testing * use standard\_attr db from neutron-lib * Update the constraints url * Use Horizon project template for django jobs * PDF documentation build * Update master for stable/train 11.0.0 ------ * Update api-ref location * Add Python 3 Train unit tests * Add local bindep.txt * Change tempest regex used in devstack-gate-bagpipe-rc * Fix bagpipe driver to work with SQLAlchemy 1.3 * fix tox python3 overrides * Remove tempest tests entry point * Move db class definitions before orm relationships to those classes * Rehome tempest tests to neutron-tempest-plugin repo * Update to opendev repository * OpenDev Migration Patch * Dropping the py35 testing * doc: Add policy reference * lower-constraints: align pyscopg version to global reqs * Replace openstack.org git:// URLs with https:// * Update master for stable/stein 10.0.0 ------ * fix tox python3 overrides * stop using common db mixin * add python 3.7 unit test job * use rpc from neutron-lib * tox: make pep8-dev use python3 like pep8 * Remove tripleo newton and ocata jobs * Upgrade pylint to a version that works with python3 * make tempest bgpvpn tests voting again * Convert policy.json into policy-in-code * use neutron-lib for model\_query * Replace tripleo scenario004-multinode with scenario004-standalone * use payloads for ROUTER\_INTERFACE BEFORE\_DELETE events * Change openstack-dev to openstack-discuss * Trival-fix: Missing parameter in declaration * Update min tox version to 2.0 * use context manager from neutron-lib * Increment versioning with pbr instruction * Remove extra publish-openstack-python-branch-tarball job * add local tox targets for pep8 and py3 * opt in for neutron-lib consumption patches * Import legacy jobs * Fix lower-constraints.txt * Drop nose dependencies * Cleanup .zuul.yaml * mark test\_router\_association\_update unit test as unreliable * tempest: reenable tests now that bug 1789878 is fixed * Remove opencontrail configurations * Remove dead code * adjust requirements * remove deprecated drivers with out-of-tree alternatives * add python 3.6 unit test job * tempest: temporarily disable some tests until bug 1789878 is fixed * switch documentation job to new PTI * import zuul job settings from project-config * Remove use\_mox directive * Update reno for stable/rocky 9.0.0 ----- * update requirements for neutron-lib 1.18.0 * Trivial fix typo of description 9.0.0.0b3 --------- * heat plugin: control 'local\_pref' of BGPVPN resource * Add Heat support for Port Associations * heat plugin: resources depend on the API extension being enabled * DB models: add standard attributes * Add release notes link in README * tempest: mark test\_port\_association\_many\_bgpvpn\_routes unstable * [dashboard] Remove old buttons to create/delete associations * switch to stestr * New tempest test added for many bgvpn routes * Add python3 django 1.11 job instead of django 2.0 job * dashboard: Fix test failures caused by django test runner 9.0.0.0b2 --------- * use new neutronclient (more) * unit tests: cleanup setup\_extension call * devstack: support non-legacy neutron * unit test fix: fix api\_extension\_path being overriden * Django 2.0 support * Change sourcing neutron l2 agent script for devstack * ref driver: use decorators for registry callbacks * DB: add missing descriptions for migration scripts * [dashboard] Modify bgpvpn router associations * [dashboard] Modify bgpvpn network associations * dashboard: use new neutronclient * move n8g-odl and n8g-bagpipe as test requirements * heat: use BGPVPN API method from neutronclient * use sub-resource API extension support * doc: update python API client documentation 9.0.0.0b1 --------- * tempest: fix test\_bgpvpn\_port\_association\_bgpvpn\_route * tempest: dynamic RT allocation * use callback registry decorators * Use ALIAS instead of LABEL * remove unused plugin.get\_plugin\_name() * [dashboard] Refactoring some common code * add lower-constraints job * Updated from global requirements * Avoid tox-install.sh * Move neutron/horizon to requirements * Updated from global requirements * Updated from global requirements * Updated from global requirements * Update the outdated links * doc update: better introduction, more links * Imported Translations from Zanata * Update doc to integrate Keystone V3 * remove use of RESOURCE\_ATTRIBUTE\_MAP * doc: update driver feature support matrix * bagpipe: documentation update * Provide missing release notes * tempest: remove now-useless workarounds for unreliable tests * tempest: use IP address ranges based on environment * Imported Translations from Zanata * add tempest test for Port Association routes of type 'bgpvpn' * Imported Translations from Zanata * Update reno for stable/queens 8.0.0 ----- * switch to use new DB facade * dsvm tempest setup: use ovsfw * Correct django template pattern in babel-django.cfg * ‘local\_pref’ can be updated in 'test\_bgpvpn\_create\_update\_delete()' * enable tempest RT update test * routes-control: DB, adjust lazy loading * check consistency of BGPVPN types in Port routes of type "bgpvpn" * 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-12.0.0/networking_bgpvpn_heat/0000775000175000017500000000000013656750625022060 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn_heat/__init__.py0000664000175000017500000000000013656750513024153 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn_heat/examples/0000775000175000017500000000000013656750625023676 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01-admin.yaml0000664000175000017500000000044713656750513030422 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-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01ter_port-tenant.yaml0000664000175000017500000000244513656750513032402 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 bgpvpn_bis: type: string description: id of BGPVPN from which to readvertise a route resources: Net1: type: OS::Neutron::Net SubNet1: type: OS::Neutron::Subnet properties: network: { get_resource: Net1 } cidr: 192.168.10.0/24 Port1: type: OS::Neutron::Port properties: network: { get_resource: Net1 } Port2: type: OS::Neutron::Port properties: network: { get_resource: Net1 } BGPVPN_port_assoc1: type: OS::Neutron::BGPVPN-PORT-ASSOCIATION properties: bgpvpn_id: { get_param: bgpvpn } port_id: { get_resource: Port1 } BGPVPN_port_assoc2: type: OS::Neutron::BGPVPN-PORT-ASSOCIATION properties: bgpvpn_id: { get_param: bgpvpn } port_id: { get_resource: Port2 } advertise_fixed_ips: false routes: - type: prefix prefix: 1.1.1.1/32 local_pref: 42 - type: bgpvpn bgpvpn_id: { get_param: bgpvpn_bis } networking-bgpvpn-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01bis_router-tenant.yaml0000664000175000017500000000145113656750513032715 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-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-00.yaml0000664000175000017500000000116713656750513027333 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-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-04-tenant.yaml0000664000175000017500000000276713656750513030635 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 } Net3: type: OS::Neutron::Net SubNet3: type: OS::Neutron::Subnet properties: network: { get_resource: Net3 } cidr: 192.168.10.0/24 Port: type: OS::Neutron::Port properties: network: { get_resource: Net3 } BGPVPN_port_assoc1: type: OS::Neutron::BGPVPN-PORT-ASSOCIATION properties: bgpvpn_id: "default_vpn" port_id: { get_resource: Port } networking-bgpvpn-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-04-admin.yaml0000664000175000017500000000050213656750513030415 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-12.0.0/networking_bgpvpn_heat/examples/bgpvpn_test-01-tenant.yaml0000664000175000017500000000107213656750513030616 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-12.0.0/networking_bgpvpn_heat/bgpvpnservice.py0000664000175000017500000003764013656750513025315 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. """ required_service_extension = 'bgpvpn' PROPERTIES = ( NAME, TYPE, DESCRIPTION, ROUTE_DISTINGUISHERS, IMPORT_TARGETS, EXPORT_TARGETS, ROUTE_TARGETS, TENANT_ID, LOCAL_PREF ) = ( 'name', 'type', 'description', 'route_distinguishers', 'import_targets', 'export_targets', 'route_targets', 'tenant_id', 'local_pref' ) 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 ), LOCAL_PREF: properties.Schema( properties.Schema.INTEGER, description=_('Default value of the BGP LOCAL_PREF for the ' 'route advertisement to this BGPVPN.'), constraints=[ constraints.Range(0, 2 ** 32 - 1), ], ) } 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()) # remove local-pref if unset, to let Neutron set a default if (self.LOCAL_PREF in props and props[self.LOCAL_PREF] is None): del props[self.LOCAL_PREF] 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. """ required_service_extension = 'bgpvpn' 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_bgpvpn_network_assoc( 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_bgpvpn_network_assoc( self.properties['bgpvpn_id'], self.resource_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_bgpvpn_network_assoc( self.properties['bgpvpn_id'], self.resource_id) class BGPVPNRouterAssoc(neutron.NeutronResource): """A resource for BGPVPNRouterAssoc in neutron. """ required_service_extension = 'bgpvpn' 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_bgpvpn_router_assoc( 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_bgpvpn_router_assoc( self.properties['bgpvpn_id'], self.resource_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_bgpvpn_router_assoc( self.properties['bgpvpn_id'], self.resource_id) class BGPVPNPortAssoc(neutron.NeutronResource): """A resource for BGPVPNPortAssoc in neutron. """ required_service_extension = 'bgpvpn-routes-control' PROPERTIES = ( BGPVPN_ID, PORT_ID, ADVERTISE_FIXED_IPS, ROUTES, ) = ( 'bgpvpn_id', 'port_id', 'advertise_fixed_ips', 'routes', ) ATTRIBUTES = ( SHOW, STATUS ) = ( 'show', 'status' ) _ROUTE_DICT_KEYS = ( TYPE, PREFIX, FROM_BGPVPN, LOCAL_PREF, ) = ( 'type', 'prefix', 'bgpvpn_id', 'local_pref' ) _ROUTE_TYPE_VALUES = ( PREFIX, BGPVPN ) = ( PREFIX, 'bgpvpn' ) properties_schema = { BGPVPN_ID: properties.Schema( properties.Schema.STRING, _('name or ID of the BGPVPN.'), required=True, constraints=[ constraints.CustomConstraint('neutron.bgpvpn') ], ), PORT_ID: properties.Schema( properties.Schema.STRING, _('Port which shall be associated with the BGPVPN.'), required=True, constraints=[ constraints.CustomConstraint('neutron.port') ] ), ADVERTISE_FIXED_IPS: properties.Schema( properties.Schema.BOOLEAN, _('whether or not the fixed IPs of he port will be advertised ' 'into the BGPVPN.'), ), ROUTES: properties.Schema( properties.Schema.LIST, _('Defines routes to advertise into the BGPVPN, and for which ' 'this port will be the nexthop'), schema=properties.Schema( properties.Schema.MAP, schema={ TYPE: properties.Schema( properties.Schema.STRING, description=_('Type of the route.'), constraints=[ constraints.AllowedValues(_ROUTE_TYPE_VALUES) ], required=True ), PREFIX: properties.Schema( properties.Schema.STRING, description=_('Prefix to readvertise.'), constraints=[ constraints.CustomConstraint('net_cidr') ] ), FROM_BGPVPN: properties.Schema( properties.Schema.STRING, description=_('BGPVPN from which to readvertise routes' '(any route carrying an RT among ' 'route_targets or import_targets ' 'of this BGPVPN, will be readvertised).' ), constraints=[ constraints.CustomConstraint('neutron.bgpvpn') ] ), LOCAL_PREF: properties.Schema( properties.Schema.INTEGER, description=_('Value of the BGP LOCAL_PREF for the ' 'routes.'), constraints=[ constraints.Range(0, 2 ** 32 - 1), ], ) } ) ) } attributes_schema = { STATUS: attributes.Schema( _('Status of bgpvpn.'), ), SHOW: attributes.Schema( _('All attributes.') ), } def validate(self): super(BGPVPNPortAssoc, self).validate() def handle_create(self): self.props = self.prepare_properties(self.properties, self.physical_resource_name()) # clean-up/preparethe routes for route in self.props.get('routes', []): # remove local-pref if unset, to let Neutron set a default if (self.LOCAL_PREF in route and route[self.LOCAL_PREF] is None): del route[self.LOCAL_PREF] if route[self.TYPE] == self.PREFIX: # routes of type 'prefix' should not have a bgpvpn_id attribute del route[self.FROM_BGPVPN] elif route[self.TYPE] == self.BGPVPN: # routes of type 'bgpvpn' should not have a 'prefix' attribute del route[self.PREFIX] # need to lookup the BGPVPN by name or id route[self.FROM_BGPVPN] = ( self.client_plugin().find_resourceid_by_name_or_id( 'bgpvpn', route[self.FROM_BGPVPN]) ) body = self.props.copy() body.pop('bgpvpn_id') bgpvpn_id = self.client_plugin().find_resourceid_by_name_or_id( 'bgpvpn', self.props['bgpvpn_id']) port_assoc = self.neutron().create_bgpvpn_port_assoc( bgpvpn_id, {'port_association': body}) self.resource_id_set(port_assoc['port_association']['id']) def handle_update(self, json_snippet, tmpl_diff, prop_diff): raise NotImplementedError() def handle_delete(self): try: self.neutron().delete_bgpvpn_port_assoc( self.properties['bgpvpn_id'], self.resource_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_port_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, 'OS::Neutron::BGPVPN-PORT-ASSOCIATION': BGPVPNPortAssoc, } networking-bgpvpn-12.0.0/networking_bgpvpn_heat/locale/0000775000175000017500000000000013656750625023317 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn_heat/locale/en_GB/0000775000175000017500000000000013656750625024271 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn_heat/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013656750625026056 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn_heat/locale/en_GB/LC_MESSAGES/networking_bgpvpn_heat.po0000664000175000017500000000610713656750513033162 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata # Andi Chandler , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn VERSION\n" "Report-Msgid-Bugs-To: https://bugs.launchpad.net/openstack-i18n/\n" "POT-Creation-Date: 2019-12-05 06:33+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-12-21 02:47+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\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 "" "BGPVPN from which to readvertise routes(any route carrying an RT among " "route_targets or import_targets of this BGPVPN, will be readvertised)." msgstr "" "BGPVPN from which to readvertise routes(any route carrying an RT among " "route_targets or import_targets of this BGPVPN, will be readvertised)." msgid "" "Default value of the BGP LOCAL_PREF for the route advertisement to this " "BGPVPN." msgstr "" "Default value of the BGP LOCAL_PREF for the route advertisement to this " "BGPVPN." msgid "" "Defines routes to advertise into the BGPVPN, and for which this port will be " "the nexthop" msgstr "" "Defines routes to advertise into the BGPVPN, and for which this port will be " "the nexthop" 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 "Port which shall be associated with the BGPVPN." msgstr "Port which shall be associated with the BGPVPN." msgid "Prefix to readvertise." msgstr "Prefix to readvertise." 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 "Type of the route." msgstr "Type of the route." msgid "Value of the BGP LOCAL_PREF for the routes." msgstr "Value of the BGP LOCAL_PREF for the routes." msgid "name or ID of the BGPVPN." msgstr "name or ID of the BGPVPN." msgid "" "whether or not the fixed IPs of he port will be advertised into the BGPVPN." msgstr "" "whether or not the fixed IPs of he port will be advertised into the BGPVPN." networking-bgpvpn-12.0.0/networking_bgpvpn_heat/_i18n.py0000664000175000017500000000211113656750513023337 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-12.0.0/.mailmap0000664000175000017500000000013113656750513016724 0ustar zuulzuul00000000000000# Format is: # # networking-bgpvpn-12.0.0/specs/0000775000175000017500000000000013656750625016431 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/specs/bgpvpn.rst0000664000175000017500000000016413656750513020454 0ustar zuulzuul00000000000000This is a work in progress to implement the specs currently discussed at: https://review.openstack.org/#/c/177740/ networking-bgpvpn-12.0.0/etc/0000775000175000017500000000000013656750625016067 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/etc/README.txt0000664000175000017500000000102313656750513017555 0ustar zuulzuul00000000000000To generate the sample networking-bgpvpn configuration files and the sample policy file, run the following commands respectively from the top level of the networking-bgpvpn directory: tox -e genconfig tox -e genpolicy If a 'tox' environment is unavailable, then you can run the following commands respectively instead to generate the configuration files: oslo-config-generator --config-file etc/oslo-config-generator/networking-bgpvpn.conf oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf networking-bgpvpn-12.0.0/etc/oslo-config-generator/0000775000175000017500000000000013656750625022272 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/etc/oslo-config-generator/networking-bgpvpn.conf0000664000175000017500000000020213656750513026610 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/neutron/networking-bgpvpn.conf.sample wrap_width = 79 namespace = networking-bgpvpn.service_provider networking-bgpvpn-12.0.0/etc/neutron/0000775000175000017500000000000013656750625017561 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/etc/neutron/networking_bgpvpn.conf0000664000175000017500000000046013656750513024167 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-12.0.0/etc/oslo-policy-generator/0000775000175000017500000000000013656750625022324 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/etc/oslo-policy-generator/policy.conf0000664000175000017500000000011513656750513024463 0ustar zuulzuul00000000000000[DEFAULT] output_file = etc/policy.yaml.sample namespace = networking-bgpvpn networking-bgpvpn-12.0.0/HACKING.rst0000664000175000017500000000024713656750513017111 0ustar zuulzuul00000000000000networking-bgpvpn Style Commandments =============================================== Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ networking-bgpvpn-12.0.0/.pylintrc0000664000175000017500000000570113656750513017160 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, len-as-condition, missing-docstring, superfluous-parens, # "R" Refactor recommendations abstract-class-little-used, abstract-class-not-used, duplicate-code, inconsistent-return-statements, interface-not-implemented, no-else-return, 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, useless-object-inheritance [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-12.0.0/releasenotes/0000775000175000017500000000000013656750625020005 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/notes/0000775000175000017500000000000013656750625021135 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/notes/deprecate-old-opencontrail-driver-a598892ddf54c724.yaml0000664000175000017500000000045413656750513032672 0ustar zuulzuul00000000000000--- 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-12.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000013656750513023402 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/notes/horizon-support-06a7b21286002949.yaml0000664000175000017500000000120213656750513027122 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-12.0.0/releasenotes/notes/drop-py-2-7-4db5f2b1529bb09c.yaml0000664000175000017500000000034013656750513026176 0ustar zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. Last release of networking-bgpvpn to support Python 2.7 is OpenStack Train. The minimum version of Python now supported by networking-bgpvpn is Python 3.6. networking-bgpvpn-12.0.0/releasenotes/notes/bagpipe_enable_evpn-ae64f77df89e069b.yaml0000664000175000017500000000017313656750513030305 0ustar zuulzuul00000000000000--- features: - | BGPVPNs of type L2 are now supported with Neutron ML2 reference drivers (OVS and linuxbridge). networking-bgpvpn-12.0.0/releasenotes/notes/add-vni-to-bgpvpn-31d6eda7ba6d5047.yaml0000664000175000017500000000021513656750513027540 0ustar zuulzuul00000000000000--- features: - | Add ``vni`` optional attribute to ``bgpvpn`` resource to control the VXLAN VNI when VXLAN encapsulation is used. networking-bgpvpn-12.0.0/releasenotes/notes/odl_router_association-fa2ed7c396531418.yaml0000664000175000017500000000010713656750513030726 0ustar zuulzuul00000000000000--- features: - OpenDaylight driver now supports Router associations networking-bgpvpn-12.0.0/releasenotes/notes/filtering-on-resource-association-2acdbc5b59d1a40a.yaml0000664000175000017500000000020113656750513033156 0ustar zuulzuul00000000000000--- features: - The API now supports filtering BGPVPN resources based on the networks or routers they are associated with. networking-bgpvpn-12.0.0/releasenotes/notes/bagpipe_ovo_rpcs-380f7bd52969bef7.yaml0000664000175000017500000000033613656750513027576 0ustar zuulzuul00000000000000--- other: - | The BGPVPN reference driver `bagpipe`, for use with Neutron linuxbridge or OVS reference drivers, has adopted OVO-based RPCs. A v2 driver is provided to avoid the production of old-style RPCs. networking-bgpvpn-12.0.0/releasenotes/notes/routes-control-api-ext-c0c4020e7370d833.yaml0000664000175000017500000000037213656750513030424 0ustar zuulzuul00000000000000--- features: - | The `bgpvpn-routes-control` API extension is introduced, allowing control of BGPVPN routing with a finer grain, including API-defined static routes pointing to Neutron ports, or BGPVPN route leaking via Neutron ports. networking-bgpvpn-12.0.0/releasenotes/notes/0_heat-support-ab233de7401aeb36.yaml0000664000175000017500000000011013656750513027135 0ustar zuulzuul00000000000000--- features: - Heat support for the whole BGPVPN Interconnection API networking-bgpvpn-12.0.0/releasenotes/notes/bagpipe-router-compat-b53b6f3799cd23db.yaml0000664000175000017500000000100613656750513030523 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-12.0.0/releasenotes/notes/mitaka-prelude-1675467c144a91ea.yaml0000664000175000017500000000025313656750513026777 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-12.0.0/releasenotes/notes/pre_commit_checks-b902ee19a3654a7b.yaml0000664000175000017500000000017713656750513027713 0ustar zuulzuul00000000000000--- features: - Pre-commit hooks were added in the driver framework, and then leveraged in BaGPipe and OpenDaylight drivers networking-bgpvpn-12.0.0/releasenotes/notes/bgpvpn_service_declaration-6d9ecd2c397e4821.yaml0000664000175000017500000000034313656750513031626 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-12.0.0/releasenotes/notes/heat_bgpvpn_local_pref-a1cbfde10810b157.yaml0000664000175000017500000000017713656750513030761 0ustar zuulzuul00000000000000--- features: - | The the 'local_pref' attribute of a Heat BGPVPN resource can now be controlled in a Heat template. networking-bgpvpn-12.0.0/releasenotes/notes/horizon-in-extras-371d572b09437dc4.yaml0000664000175000017500000000073313656750513027476 0ustar zuulzuul00000000000000--- upgrade: - | Horizon is now an optional dependency of networking-bgpvpn as the GUI support is optional. This means horizon will not be installed automatically. The horizon dependency is now declared in ``extras`` section in ``setup.cfg``. If you would like to enable the GUI support, you can install the dependencies using ``python -m pip install networking-bgpvpn[horizon]`` (or ``.[horizon]`` in case you install it from the source code). networking-bgpvpn-12.0.0/releasenotes/notes/remove_obsolete_drivers-3706e080098a5cb6.yaml0000664000175000017500000000025313656750513031020 0ustar zuulzuul00000000000000--- other: - | The obsolete in-tree drivers for OpenContrail and OpenDaylight have been removed, in favor of the out-of-tree drivers provided by these projects. networking-bgpvpn-12.0.0/releasenotes/notes/heat_port_associations-f2d316f3b8c755fe.yaml0000664000175000017500000000021113656750513031060 0ustar zuulzuul00000000000000--- features: - | The Heat plugin for the BGPVPN interconnection API extension now supports BGPVPN Port Association resources. networking-bgpvpn-12.0.0/releasenotes/notes/bagpipe-driver-improvements-401a7ba59a6f5f45.yaml0000664000175000017500000000065713656750513031667 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-12.0.0/releasenotes/source/0000775000175000017500000000000013656750625021305 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/train.rst0000664000175000017500000000017613656750513023154 0ustar zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train networking-bgpvpn-12.0.0/releasenotes/source/_static/0000775000175000017500000000000013656750625022733 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000013656750513025200 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/conf.py0000664000175000017500000002160213656750513022601 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-12.0.0/releasenotes/source/mitaka.rst0000664000175000017500000000022313656750513023276 0ustar zuulzuul00000000000000=================================== Mitaka Series Release Notes =================================== .. release-notes:: :branch: stable/mitaka networking-bgpvpn-12.0.0/releasenotes/source/liberty.rst0000664000175000017500000000022213656750513023501 0ustar zuulzuul00000000000000============================== Liberty Series Release Notes ============================== .. release-notes:: :branch: origin/stable/liberty networking-bgpvpn-12.0.0/releasenotes/source/stein.rst0000664000175000017500000000022113656750513023150 0ustar zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein networking-bgpvpn-12.0.0/releasenotes/source/queens.rst0000664000175000017500000000022313656750513023330 0ustar zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens networking-bgpvpn-12.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000015313656750513024161 0ustar zuulzuul00000000000000============================ Current Series Release Notes ============================ .. release-notes:: networking-bgpvpn-12.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022113656750513023155 0ustar zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky networking-bgpvpn-12.0.0/releasenotes/source/index.rst0000664000175000017500000000035713656750513023147 0ustar zuulzuul00000000000000================================ Networking-bgpvpn Release Notes ================================ .. toctree:: :maxdepth: 1 unreleased train stein rocky queens mitaka pike ocata newton mitaka liberty networking-bgpvpn-12.0.0/releasenotes/source/ocata.rst0000664000175000017500000000023013656750513023115 0ustar zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata networking-bgpvpn-12.0.0/releasenotes/source/newton.rst0000664000175000017500000000022313656750513023342 0ustar zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: stable/newton networking-bgpvpn-12.0.0/releasenotes/source/_templates/0000775000175000017500000000000013656750625023442 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000013656750513025707 0ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/locale/0000775000175000017500000000000013656750625022544 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/locale/en_GB/0000775000175000017500000000000013656750625023516 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000013656750625025303 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000664000175000017500000002267613656750513030345 0ustar zuulzuul00000000000000# Andi Chandler , 2017. #zanata # Andi Chandler , 2018. #zanata # Andi Chandler , 2019. #zanata msgid "" msgstr "" "Project-Id-Version: networking-bgpvpn\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-12-05 06:33+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2019-12-21 02:46+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "10.0.0" msgstr "10.0.0" msgid "11.0.0-7" msgstr "11.0.0-7" 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" msgstr "8.0.0" msgid "8.0.1" msgstr "8.0.1" msgid "9.0.0" msgstr "9.0.0" msgid "" "Add ``vni`` optional attribute to ``bgpvpn`` resource to control the VXLAN " "VNI when VXLAN encapsulation is used." msgstr "" "Add ``vni`` optional attribute to ``bgpvpn`` resource to control the VXLAN " "VNI when VXLAN encapsulation is used." msgid "" "BGPVPNs of type L2 are now supported with Neutron ML2 reference drivers (OVS " "and linuxbridge)." msgstr "" "BGPVPNs of type L2 are now supported with Neutron ML2 reference drivers (OVS " "and linuxbridge)." 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 "Deprecation Notes" msgstr "Deprecation 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 "" "Python 2.7 support has been dropped. Last release of networking-bgpvpn to " "support Python 2.7 is OpenStack Train. The minimum version of Python now " "supported by networking-bgpvpn is Python 3.6." msgstr "" "Python 2.7 support has been dropped. Last release of Networking-bgpvpn to " "support Python 2.7 is OpenStack Train. The minimum version of Python now " "supported by Networking-bgpvpn is Python 3.6." msgid "Queens Series Release Notes" msgstr "Queens Series Release Notes" msgid "Rocky Series Release Notes" msgstr "Rocky Series Release Notes" msgid "Stein Series Release Notes" msgstr "Stein Series 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 BGPVPN reference driver `bagpipe`, for use with Neutron linuxbridge or " "OVS reference drivers, has adopted OVO-based RPCs. A v2 driver is provided " "to avoid the production of old-style RPCs." msgstr "" "The BGPVPN reference driver `bagpipe`, for use with Neutron linuxbridge or " "OVS reference drivers, has adopted OVO-based RPCs. A v2 driver is provided " "to avoid the production of old-style RPCs." msgid "" "The Heat plugin for the BGPVPN interconnection API extension now supports " "BGPVPN Port Association resources." msgstr "" "The Heat plugin for the BGPVPN interconnection API extension now supports " "BGPVPN Port Association resources." msgid "" "The `bgpvpn-routes-control` API extension is introduced, allowing control of " "BGPVPN routing with a finer grain, including API-defined static routes " "pointing to Neutron ports, or BGPVPN route leaking via Neutron ports." msgstr "" "The `bgpvpn-routes-control` API extension is introduced, allowing control of " "BGPVPN routing with a finer grain, including API-defined static routes " "pointing to Neutron ports, or BGPVPN route leaking via Neutron ports." 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 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." msgstr "" "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." msgid "" "The obsolete in-tree drivers for OpenContrail and OpenDaylight have been " "removed, in favor of the out-of-tree drivers provided by these projects." msgstr "" "The obsolete in-tree drivers for OpenContrail and OpenDaylight have been " "removed, in favor of the out-of-tree drivers provided by these projects." 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 "" "The the 'local_pref' attribute of a Heat BGPVPN resource can now be " "controlled in a Heat template." msgstr "" "The the 'local_pref' attribute of a Heat BGPVPN resource can now be " "controlled in a Heat template." msgid "Train Series Release Notes" msgstr "Train Series Release Notes" msgid "Upgrade Notes" msgstr "Upgrade Notes" 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-12.0.0/releasenotes/source/pike.rst0000664000175000017500000000021713656750513022763 0ustar zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike networking-bgpvpn-12.0.0/PKG-INFO0000664000175000017500000000572513656750625016422 0ustar zuulzuul00000000000000Metadata-Version: 2.1 Name: networking-bgpvpn Version: 12.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-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/networking-bgpvpn.svg :target: https://governance.openstack.org/tc/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 SDN controllers may be used as backends, while offering the same tenant facing API. A reference implementation working along with Neutron reference drivers is also provided. * Free software: Apache license * Source: https://opendev.org/openstack/networking-bgpvpn * Bugs: https://bugs.launchpad.net/bgpvpn * Doc: https://docs.openstack.org/networking-bgpvpn/latest/ * Release notes: https://docs.openstack.org/releasenotes/networking-bgpvpn/ =================== Introduction videos =================== The following videos are filmed presentations of talks given during the Barcelona OpenStack Summit (Oct' 2016). Although they do not cover the work done since, they can be a good introduction to the project: * https://www.youtube.com/watch?v=kGW5R8mtmRg * https://www.youtube.com/watch?v=LCDeR7MwTzE 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 :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 Provides-Extra: bagpipe Provides-Extra: horizon Provides-Extra: test networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/0000775000175000017500000000000013656750625022551 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/entry_points.txt0000664000175000017500000000142013656750624026043 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.policies] networking-bgpvpn = networking_bgpvpn.policies:list_rules [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.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 [oslo.policy.policies] networking-bgpvpn = networking_bgpvpn.policies:list_rules networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/dependency_links.txt0000664000175000017500000000000113656750624026616 0ustar zuulzuul00000000000000 networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/requires.txt0000664000175000017500000000105113656750624025145 0ustar zuulzuul00000000000000pbr>=4.0.0 oslo.config>=5.2.0 oslo.db>=4.37.0 oslo.i18n>=3.15.3 oslo.log>=3.36.0 oslo.utils>=3.33.0 neutron-lib>=1.30.0 debtcollector>=1.19.0 neutron>=13.0.0 [bagpipe] networking-bagpipe>=9.0.0 [horizon] horizon>=17.1.0 [test] hacking!=0.13.0,<0.14,>=0.12.0 coverage!=4.4,>=4.0 python-subunit>=1.0.0 psycopg2>=2.7.7 PyMySQL>=0.7.6 WebOb>=1.8.2 oslotest>=3.2.0 stestr>=1.0.0 testresources>=2.0.0 testscenarios>=0.4 testtools>=2.2.0 tempest>=17.1.0 networking-bagpipe>=9.0.0 horizon>=17.1.0 [test:(python_version>="3.0")] astroid==2.1.0 pylint==2.2.0 networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/pbr.json0000664000175000017500000000005613656750624024227 0ustar zuulzuul00000000000000{"git_version": "d6a93f6", "is_release": true}networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/PKG-INFO0000664000175000017500000000572513656750624023656 0ustar zuulzuul00000000000000Metadata-Version: 2.1 Name: networking-bgpvpn Version: 12.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-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/networking-bgpvpn.svg :target: https://governance.openstack.org/tc/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 SDN controllers may be used as backends, while offering the same tenant facing API. A reference implementation working along with Neutron reference drivers is also provided. * Free software: Apache license * Source: https://opendev.org/openstack/networking-bgpvpn * Bugs: https://bugs.launchpad.net/bgpvpn * Doc: https://docs.openstack.org/networking-bgpvpn/latest/ * Release notes: https://docs.openstack.org/releasenotes/networking-bgpvpn/ =================== Introduction videos =================== The following videos are filmed presentations of talks given during the Barcelona OpenStack Summit (Oct' 2016). Although they do not cover the work done since, they can be a good introduction to the project: * https://www.youtube.com/watch?v=kGW5R8mtmRg * https://www.youtube.com/watch?v=LCDeR7MwTzE 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 :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 Provides-Extra: bagpipe Provides-Extra: horizon Provides-Extra: test networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/top_level.txt0000664000175000017500000000007213656750624025301 0ustar zuulzuul00000000000000bgpvpn_dashboard networking_bgpvpn networking_bgpvpn_heat networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/not-zip-safe0000664000175000017500000000000113656750624024776 0ustar zuulzuul00000000000000 networking-bgpvpn-12.0.0/networking_bgpvpn.egg-info/SOURCES.txt0000664000175000017500000003227113656750625024442 0ustar zuulzuul00000000000000.coveragerc .mailmap .pylintrc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst babel-django.cfg babel-djangojs.cfg babel.cfg bindep.txt lower-constraints.txt 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/tabs.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/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/tabs.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/network_associations/__init__.py bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/tables.py bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/tabs.py bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/urls.py bgpvpn_dashboard/dashboards/project/bgpvpn/network_associations/views.py bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/__init__.py bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/forms.py bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/tables.py bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/tabs.py bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/urls.py bgpvpn_dashboard/dashboards/project/bgpvpn/router_associations/views.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/_create_network_association.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/create_network_association.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/index.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/modify.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/network_associations/_detail_overview.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/_detail_overview.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/_modify.html bgpvpn_dashboard/dashboards/project/bgpvpn/templates/bgpvpn/router_associations/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/en_GB/LC_MESSAGES/django.po 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/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/test_bgpvpn.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/plugin.sh devstack/settings doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/introduction.rst doc/source/_static/.placeholder doc/source/configuration/index.rst doc/source/configuration/networking-bgpvpn.rst doc/source/configuration/policy-sample.rst doc/source/configuration/policy.rst doc/source/configuration/samples/networking-bgpvpn.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/README.txt etc/neutron/networking_bgpvpn.conf etc/oslo-config-generator/networking-bgpvpn.conf etc/oslo-policy-generator/policy.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/locale/en_GB/LC_MESSAGES/networking_bgpvpn.po 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/db/migration/alembic_migrations/versions/rocky/contract/9d7f1ae5fa56_add_standard_attributes.py networking_bgpvpn/neutron/db/migration/alembic_migrations/versions/rocky/expand/7a9482036ecd_add_standard_attributes.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/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/policies/__init__.py networking_bgpvpn/policies/base.py networking_bgpvpn/policies/bgpvpn.py networking_bgpvpn/policies/network_association.py networking_bgpvpn/policies/port_association.py networking_bgpvpn/policies/router_association.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_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-01ter_port-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 playbooks/networking-bgpvpn-bagpipe-dsvm-install/post.yaml playbooks/networking-bgpvpn-bagpipe-dsvm-install/run.yaml playbooks/networking-bgpvpn-dsvm-install/post.yaml playbooks/networking-bgpvpn-dsvm-install/run.yaml 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/bagpipe_ovo_rpcs-380f7bd52969bef7.yaml releasenotes/notes/bgpvpn_service_declaration-6d9ecd2c397e4821.yaml releasenotes/notes/deprecate-old-opencontrail-driver-a598892ddf54c724.yaml releasenotes/notes/drop-py-2-7-4db5f2b1529bb09c.yaml releasenotes/notes/filtering-on-resource-association-2acdbc5b59d1a40a.yaml releasenotes/notes/heat_bgpvpn_local_pref-a1cbfde10810b157.yaml releasenotes/notes/heat_port_associations-f2d316f3b8c755fe.yaml releasenotes/notes/horizon-in-extras-371d572b09437dc4.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/notes/remove_obsolete_drivers-3706e080098a5cb6.yaml releasenotes/notes/routes-control-api-ext-c0c4020e7370d833.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/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po specs/bgpvpn.rst tools/django-manage.py tools/generate_config_file_samples.shnetworking-bgpvpn-12.0.0/bindep.txt0000664000175000017500000000060113656750513017307 0ustar zuulzuul00000000000000# This is a cross-platform list tracking distribution packages needed for install and tests; # see https://docs.openstack.org/infra/bindep/ for additional information. libpq-dev [platform:dpkg] mysql-client [platform:dpkg] mysql-server [platform:dpkg] postgresql [test] postgresql-client [platform:dpkg test] postgresql-devel [platform:rpm test] postgresql-server [platform:rpm test] networking-bgpvpn-12.0.0/test-requirements.txt0000664000175000017500000000174013656750513021553 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 python-subunit>=1.0.0 # Apache-2.0/BSD psycopg2>=2.7.7 # LGPL/ZPL PyMySQL>=0.7.6 # MIT License WebOb>=1.8.2 # MIT oslotest>=3.2.0 # Apache-2.0 astroid==2.1.0;python_version>="3.0" # LGPLv2.1 pylint==2.2.0;python_version>="3.0" # GPLv2 stestr>=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 tempest>=17.1.0 # Apache-2.0 # OpenStack CI will install the following projects from git # if they are in the required-projects list for a job. # Installation by 'extras' in tox.ini does not honor upper-constraints, # so we specify the same here to ensure upper-constraints. networking-bagpipe>=9.0.0 # Apache-2.0 horizon>=17.1.0 # Apache-2.0 networking-bgpvpn-12.0.0/lower-constraints.txt0000664000175000017500000000744113656750513021554 0ustar zuulzuul00000000000000alabaster==0.7.10 alembic==0.8.10 amqp==2.1.1 appdirs==1.4.3 asn1crypto==0.23.0 astroid==1.6.5 Babel==2.3.4 beautifulsoup4==4.6.0 blockdiag==1.5.3 cachetools==2.0.0 cffi==1.7.0 cliff==2.8.0 cmd2==0.8.0 contextlib2==0.4.0 coverage==4.0 cryptography==2.1 debtcollector==1.19.0 decorator==3.4.0 deprecation==1.0 Django==2.2 django-appconf==1.0.2 django-babel==0.6.2 django-compressor==2.0 django-pyscss==2.0.2 docutils==0.11 dogpile.cache==0.6.2 eventlet==0.18.2 exabgp==4.0.4 extras==1.0.0 fasteners==0.7.0 fixtures==3.0.0 flake8==2.5.5 funcparserlib==0.3.6 future==0.16.0 futurist==1.2.0 greenlet==0.4.10 hacking==0.12.0 horizon==17.1.0 httplib2==0.9.1 idna==2.6 imagesize==0.7.1 iso8601==0.1.11 Jinja2==2.10 jmespath==0.9.0 jsonpatch==1.16 jsonpointer==1.13 jsonschema==2.6.0 keystoneauth1==3.14.0 keystonemiddleware==4.17.0 kombu==4.0.0 linecache2==1.0.0 logilab-common==1.4.1 logutils==0.3.5 Mako==0.4.0 MarkupSafe==1.0 mccabe==0.2.1 mock==2.0.0 monotonic==0.6 mox3==0.20.0 msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 neutron-lib==1.30.0 openstacksdk==0.31.2 os-client-config==1.28.0 os-ken==0.3.0 os-service-types==1.7.0 os-testr==1.0.0 os-traits==0.9.0 os-xenapi==0.3.1 os-vif==1.15.1 osc-lib==1.8.0 oslo.cache==1.26.0 oslo.concurrency==3.26.0 oslo.config==5.2.0 oslo.context==2.19.2 oslo.db==4.37.0 oslo.i18n==3.15.3 oslo.log==3.36.0 oslo.messaging==5.29.0 oslo.middleware==3.31.0 oslo.policy==1.30.0 oslo.privsep==1.32.0 oslo.reports==1.18.0 oslo.rootwrap==5.8.0 oslo.serialization==2.18.0 oslo.service==1.24.0 oslo.upgradecheck==0.1.1 oslo.utils==3.33.0 oslo.versionedobjects==1.35.1 oslotest==3.2.0 osprofiler==2.3.0 ovs==2.8.0 ovsdbapp==0.17.0 paramiko==2.0.0 Paste==2.0.2 PasteDeploy==1.5.0 pbr==4.0.0 pecan==1.3.2 pep8==1.5.7 pika==0.10.0 pika-pool==0.1.3 Pillow==2.4.0 Pint==0.5 positional==1.2.1 prettytable==0.7.2 psutil==3.2.2 psycopg2==2.7.7 pyasn1==0.1.8 pycadf==1.1.0 pycparser==2.18 pyflakes==0.8.1 Pygments==2.2.0 pyinotify==0.9.6 pylint==1.9.2 pymongo==3.0.2 PyMySQL==0.7.6 pyOpenSSL==17.1.0 pyparsing==2.1.0 pyperclip==1.5.27 pyroute2==0.5.7 pyScss==1.3.7 python-cinderclient==5.0.0 python-dateutil==2.5.3 python-designateclient==2.7.0 python-editor==1.0.3 python-glanceclient==2.8.0 python-keystoneclient==3.22.0 python-mimeparse==1.6.0 python-neutronclient==6.7.0 python-novaclient==9.1.0 python-subunit==1.0.0 python-swiftclient==3.2.0 pytz==2013.6 PyYAML==3.12 rcssmin==1.0.6 repoze.lru==0.7 requests==2.14.2 requestsexceptions==1.2.0 rfc3986==0.3.1 rjsmin==1.0.12 Routes==2.3.1 ryu==4.24 semantic-version==2.3.1 seqdiag==0.9.5 simplejson==3.5.1 six==1.10.0 snowballstemmer==1.2.1 Sphinx==1.6.2 SQLAlchemy==1.2.0 sqlalchemy-migrate==0.11.0 sqlparse==0.2.2 statsd==3.2.1 stestr==1.0.0 stevedore==1.20.0 tempest==17.1.0 Tempita==0.5.2 tenacity==4.4.0 testrepository==0.0.18 testresources==2.0.0 testscenarios==0.4 testtools==2.2.0 tinyrpc==0.6 tooz==1.58.0 traceback2==1.4.0 urllib3==1.21.1 vine==1.1.4 waitress==1.1.0 warlock==1.2.0 webcolors==1.7 WebOb==1.8.2 websocket-client==0.40.0 WebTest==2.0.27 wrapt==1.7.0 XStatic==1.0.0 XStatic-Angular==1.5.8.0 XStatic-Angular-Bootstrap==2.2.0.0 XStatic-Angular-FileUpload==12.0.4.0 XStatic-Angular-Gettext==2.3.8.0 XStatic-Angular-lrdragndrop==1.0.2.2 XStatic-Angular-Schema-Form==0.8.13.0 XStatic-Bootstrap-Datepicker==1.3.1.0 XStatic-Bootstrap-SCSS==3.3.7.1 XStatic-bootswatch==3.3.7.0 XStatic-D3==3.5.17.0 XStatic-Font-Awesome==4.7.0.0 XStatic-Hogan==2.0.0.2 XStatic-Jasmine==2.4.1.1 XStatic-jQuery==1.8.2.1 XStatic-JQuery-Migrate==1.2.1.1 XStatic-jquery-ui==1.10.4.1 XStatic-JQuery.quicksearch==2.0.3.1 XStatic-JQuery.TableSorter==2.14.5.1 XStatic-JSEncrypt==2.3.1.1 XStatic-mdi==1.4.57.0 XStatic-objectpath==1.2.1.0 XStatic-Rickshaw==1.5.0.0 XStatic-roboto-fontface==0.5.0.0 XStatic-smart-table==1.4.13.2 XStatic-Spin==1.2.5.2 XStatic-term.js==0.0.7.0 XStatic-tv4==1.2.7.0 networking-bgpvpn-12.0.0/AUTHORS0000664000175000017500000000771313656750624016373 0ustar zuulzuul00000000000000Akihiro Motoki Andreas Jaeger Andrzej Denisiewicz Anh Tran Antoine Eiche Armando Migliaccio Atsushi SAKAI Bernard Cafarelli Bin-Lu <369283883@qq.com> Bob Melander Boden R Cao Xuan Hoang Cedric Savignan Chuck Short Claire Delcourt Corey Bryant Cédric Savignan Daniel Mellado Dariusz Smigiel Davanum Srinivas David Blaisonneau Deepthi V V Doug Hellmann Edouard Thuleau Elod Illes Emilien Macchi Flavio Percoco Francois Eleouet Georg Kunz Ghanshyam Mann Henry Henry Gessau Henry Gessau Ian Wienand Ihar Hrachyshka James E. Blair Jean-Philippe Braun Jeremy Stanley Ji-Wei Joan Meseguer Jon Schlueter Kailun Qin Ken'ichi Ohmichi Le Hou Li-zhigang Luke Hinds Luong Anh Tuan Marios Andreou Masayuki Igawa Mathieu Rohon Monty Taylor Morgan Richomme Ngo Quoc Cuong Nguyen Hung Phuong Nguyen Van Trung Nikolas Hermanns Nishant Kumar Oleg Bondarev Omar Sanhaji OpenStack Release Bot Paul Carver Pawel Rusak Peter V. Saveliev Pierre Crégut Rafael Folco Ramanjaneya Romanos Skiadas Sean McGinnis Slawek Kaplonski Thomas Monguillon Thomas Morin Thomas Morin Tuan Do Anh Vieri <15050873171@163.com> Vishal Thapar Vivekanandan Narasimhan Vu Cong Tuan Wim De Clercq YAMAMOTO Takashi bfernando bhargavaregalla chenxing davidblaisonneau-orange elajkat gengchc2 ghanshyam huang.zhiping janonymous lidong malei manchandavishal mathieu-rohon melissaml pengyuesheng qinchunhua ravikiran suresh kumar yatin ythomas1 zhangyanxian zhaojingjing0067370 Édouard Thuleau Édouard Thuleau Łukasz Rajewski networking-bgpvpn-12.0.0/babel.cfg0000664000175000017500000000002113656750513017027 0ustar zuulzuul00000000000000[python: **.py] networking-bgpvpn-12.0.0/CONTRIBUTING.rst0000664000175000017500000000103313656750513017746 0ustar zuulzuul00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: https://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: https://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-12.0.0/tools/0000775000175000017500000000000013656750625016454 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/tools/generate_config_file_samples.sh0000775000175000017500000000144013656750513024650 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-12.0.0/tools/django-manage.py0000775000175000017500000000150013656750513021511 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-12.0.0/README.rst0000664000175000017500000000342413656750513017002 0ustar zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/networking-bgpvpn.svg :target: https://governance.openstack.org/tc/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 SDN controllers may be used as backends, while offering the same tenant facing API. A reference implementation working along with Neutron reference drivers is also provided. * Free software: Apache license * Source: https://opendev.org/openstack/networking-bgpvpn * Bugs: https://bugs.launchpad.net/bgpvpn * Doc: https://docs.openstack.org/networking-bgpvpn/latest/ * Release notes: https://docs.openstack.org/releasenotes/networking-bgpvpn/ =================== Introduction videos =================== The following videos are filmed presentations of talks given during the Barcelona OpenStack Summit (Oct' 2016). Although they do not cover the work done since, they can be a good introduction to the project: * https://www.youtube.com/watch?v=kGW5R8mtmRg * https://www.youtube.com/watch?v=LCDeR7MwTzE networking-bgpvpn-12.0.0/babel-djangojs.cfg0000664000175000017500000000006113656750513020630 0ustar zuulzuul00000000000000[javascript: **.js] [angular: **/static/**.html] networking-bgpvpn-12.0.0/.zuul.yaml0000664000175000017500000001122713656750513017254 0ustar zuulzuul00000000000000- job: name: networking-bgpvpn-functional-bagpipe parent: neutron-functional required-projects: - openstack/neutron - openstack/networking-bgpvpn - openstack/networking-bagpipe vars: project_name: networking-bgpvpn devstack_localrc: NETWORKING_BGPVPN_DRIVER: BGPVPN:BaGPipe:networking_bgpvpn.neutron.services.service_drivers.bagpipe.bagpipe_v2.BaGPipeBGPVPNDriver:default BAGPIPE_DATAPLANE_DRIVER_IPVPN: ovs BAGPIPE_BGP_PEERS: "-" devstack_plugins: networking-bagpipe: https://opendev.org/openstack/networking-bagpipe networking-bgpvpn: https://opendev.org/openstack/networking-bgpvpn neutron: https://opendev.org/openstack/neutron - job: name: networking-bgpvpn-functional-full parent: neutron-functional required-projects: - openstack/neutron - openstack/networking-bgpvpn - openstack/networking-bagpipe - openstack/networking-odl - openstack/horizon vars: project_name: networking-bgpvpn devstack_plugins: networking-bgpvpn: https://opendev.org/openstack/networking-bgpvpn neutron: https://opendev.org/openstack/neutron - job: name: networking-bgpvpn-bagpipe-dsvm-install parent: legacy-dsvm-base run: playbooks/networking-bgpvpn-bagpipe-dsvm-install/run.yaml post-run: playbooks/networking-bgpvpn-bagpipe-dsvm-install/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/networking-bagpipe - openstack/networking-bgpvpn - job: name: networking-bgpvpn-dsvm-install parent: legacy-dsvm-base run: playbooks/networking-bgpvpn-dsvm-install/run.yaml post-run: playbooks/networking-bgpvpn-dsvm-install/post.yaml timeout: 7800 required-projects: - openstack/devstack-gate - openstack/networking-bgpvpn - project: templates: - openstack-python3-ussuri-jobs-neutron - publish-openstack-docs-pti - release-notes-jobs-python3 - periodic-stable-jobs-neutron - check-requirements - horizon-non-primary-django-jobs 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-container will # run in Pike 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-standalone: voting: false - openstack-tox-lower-constraints: required-projects: - openstack/neutron - openstack/networking-bagpipe - openstack/horizon - openstack-tox-pep8: required-projects: - openstack/networking-bagpipe - openstack/horizon - openstack-tox-py36: required-projects: - openstack/networking-bagpipe - openstack/horizon - openstack-tox-py37: required-projects: - openstack/networking-bagpipe - openstack/horizon - openstack-tox-py38: required-projects: - openstack/networking-bagpipe - openstack/horizon - neutron-tempest-plugin-bgpvpn-bagpipe: irrelevant-files: - ^(test-|)requirements.txt$ - ^setup.cfg$ - openstack-tox-cover: required-projects: - openstack/neutron - openstack/networking-bagpipe - openstack/horizon - networking-bgpvpn-functional-full gate: jobs: - openstack-tox-lower-constraints: required-projects: - openstack/neutron - openstack/networking-bagpipe - openstack/horizon - openstack-tox-pep8: required-projects: - openstack/networking-bagpipe - openstack/horizon - openstack-tox-py36: required-projects: - openstack/networking-bagpipe - openstack/horizon - openstack-tox-py37: required-projects: - openstack/networking-bagpipe - openstack/horizon - neutron-tempest-plugin-bgpvpn-bagpipe: irrelevant-files: - ^(test-|)requirements.txt$ - ^setup.cfg$ - networking-bgpvpn-functional-full experimental: jobs: - networking-bgpvpn-dsvm-install - networking-bgpvpn-bagpipe-dsvm-install - networking-bgpvpn-functional-bagpipe networking-bgpvpn-12.0.0/setup.py0000664000175000017500000000200613656750513017020 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-12.0.0/.stestr.conf0000664000175000017500000000011713656750513017560 0ustar zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./networking_bgpvpn/tests/unit} top_dir=./ networking-bgpvpn-12.0.0/devstack/0000775000175000017500000000000013656750625017120 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/devstack/settings0000775000175000017500000000053013656750513020700 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-12.0.0/devstack/devstack-gate-rc0000664000175000017500000000034313656750513022163 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=neutron-api,neutron-agent,neutron-dhcp,neutron-l3,key,mysql,rabbit networking-bgpvpn-12.0.0/devstack/devstack-gate-bagpipe-rc0000664000175000017500000000346313656750513023576 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-api-meta,n-sch,placement-api,g-api,g-reg,neutron-api,neutron-agent,neutron-dhcp,neutron-l3,neutron-metadata-agent,neutron-bagpipe-bgp,key,mysql,rabbit if [[ $DEVSTACK_GATE_TEMPEST -eq 1 ]] ; then export DEVSTACK_GATE_TEMPEST_ALL_PLUGINS=1 export DEVSTACK_GATE_TEMPEST_REGEX="^neutron_tempest_plugin\.bgpvpn" 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://opendev.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 https://opendev.org/openstack/neutron" # Using the openvswitch firewall driver for security groups is currently # required to allow BGPVPN/router coexistence in single node or DVR setup export DEVSTACK_LOCAL_CONFIG+=$'\n'"[[post-config|/etc/neutron/plugins/ml2/ml2_conf.ini]]" export DEVSTACK_LOCAL_CONFIG+=$'\n'"[securitygroup]" export DEVSTACK_LOCAL_CONFIG+=$'\n'"firewall_driver = openvswitch" networking-bgpvpn-12.0.0/devstack/plugin.sh0000775000175000017500000000770313656750513020760 0ustar zuulzuul00000000000000#!/bin/bash # Save trace setting _XTRACE_NETWORKING_BGPVPN=$(set +o | grep xtrace) set -o xtrace 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" == "post-config" ]]; then if is_service_enabled neutron-api || is_service_enabled q-svc; then echo_summary "Configuring networking-bgpvpn" neutron_service_plugin_class_add bgpvpn 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 neutron-agent || is_service_enabled q-agt) && (is_service_enabled b-bgp || is_service_enabled neutron-bagpipe-bgp); then echo_summary "Configuring agent for bagpipe bgpvpn" source $NEUTRON_DIR/devstack/lib/l2_agent plugin_agent_add_l2_agent_extension bagpipe_bgpvpn configure_l2_agent if is_neutron_legacy_enabled; then 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 else if [[ "$NEUTRON_AGENT" == "openvswitch" ]]; then # l2pop and arp_responder are required for bagpipe driver iniset $NEUTRON_CORE_PLUGIN_CONF agent l2_population True iniset $NEUTRON_CORE_PLUGIN_CONF agent arp_responder True elif [[ "$NEUTRON_AGENT" == "linuxbridge" ]]; then # l2pop is required for EVPN/VXLAN bagpipe driver iniset $NEUTRON_CORE_PLUGIN_CONF vxlan l2_population True else die $LINENO "unsupported agent for networking-bagpipe: $NEUTRON_AGENT" fi 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-12.0.0/LICENSE0000664000175000017500000002363713656750513016330 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-12.0.0/playbooks/0000775000175000017500000000000013656750625017317 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/playbooks/networking-bgpvpn-dsvm-install/0000775000175000017500000000000013656750625025413 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/playbooks/networking-bgpvpn-dsvm-install/run.yaml0000664000175000017500000000417013656750513027101 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-networking-bgpvpn-dsvm-install from old job gate-networking-bgpvpn-dsvm-install-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] enable_plugin networking-bgpvpn https://opendev.org/openstack/networking-bgpvpn EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi # Because we are testing a non standard project, add # our project repository. This makes zuul do the right # reference magic for testing changes. export PROJECTS="openstack/networking-bgpvpn $PROJECTS" # Keep localrc to be able to set some vars in pre_test_hook export KEEP_LOCALRC=1 export DEVSTACK_GATE_SETTINGS=/opt/stack/new/networking-bgpvpn/devstack/devstack-gate-rc cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' networking-bgpvpn-12.0.0/playbooks/networking-bgpvpn-dsvm-install/post.yaml0000664000175000017500000000063313656750513027262 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs networking-bgpvpn-12.0.0/playbooks/networking-bgpvpn-bagpipe-dsvm-install/0000775000175000017500000000000013656750625027020 5ustar zuulzuul00000000000000networking-bgpvpn-12.0.0/playbooks/networking-bgpvpn-bagpipe-dsvm-install/run.yaml0000664000175000017500000000432713656750513030512 0ustar zuulzuul00000000000000- hosts: all name: Autoconverted job legacy-networking-bgpvpn-bagpipe-dsvm-install from old job gate-networking-bgpvpn-bagpipe-dsvm-install-ubuntu-xenial tasks: - name: Ensure legacy workspace directory file: path: '{{ ansible_user_dir }}/workspace' state: directory - shell: cmd: | set -e set -x cat > clonemap.yaml << EOF clonemap: - name: openstack/devstack-gate dest: devstack-gate EOF /usr/zuul-env/bin/zuul-cloner -m clonemap.yaml --cache-dir /opt/git \ https://opendev.org \ openstack/devstack-gate executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x cat << 'EOF' >>"/tmp/dg-local.conf" [[local|localrc]] enable_plugin networking-bgpvpn https://opendev.org/openstack/networking-bgpvpn EOF executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' - shell: cmd: | set -e set -x export PYTHONUNBUFFERED=true export DEVSTACK_GATE_NEUTRON=1 export BRANCH_OVERRIDE=default if [ "$BRANCH_OVERRIDE" != "default" ] ; then export OVERRIDE_ZUUL_BRANCH=$BRANCH_OVERRIDE fi # Because we are testing a non standard project, add # our project repository. This makes zuul do the right # reference magic for testing changes. export PROJECTS="openstack/networking-bgpvpn $PROJECTS" export PROJECTS="openstack/networking-bagpipe $PROJECTS" # Keep localrc to be able to set some vars in pre_test_hook export KEEP_LOCALRC=1 export DEVSTACK_GATE_SETTINGS=/opt/stack/new/networking-bgpvpn/devstack/devstack-gate-bagpipe-rc cp devstack-gate/devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh ./safe-devstack-vm-gate-wrap.sh executable: /bin/bash chdir: '{{ ansible_user_dir }}/workspace' environment: '{{ zuul | zuul_legacy_vars }}' networking-bgpvpn-12.0.0/playbooks/networking-bgpvpn-bagpipe-dsvm-install/post.yaml0000664000175000017500000000063313656750513030667 0ustar zuulzuul00000000000000- hosts: primary tasks: - name: Copy files from {{ ansible_user_dir }}/workspace/ on node synchronize: src: '{{ ansible_user_dir }}/workspace/' dest: '{{ zuul.executor.log_root }}' mode: pull copy_links: true verify_host: true rsync_opts: - --include=/logs/** - --include=*/ - --exclude=* - --prune-empty-dirs networking-bgpvpn-12.0.0/.coveragerc0000664000175000017500000000021513656750513017427 0ustar zuulzuul00000000000000[run] branch = True source = networking-bgpvpn omit = networking-bgpvpn/tests/*,networking-bgpvpn/openstack/* [report] ignore_errors = True