././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/0000775000175000017500000000000000000000000016415 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/.coveragerc0000664000175000017500000000017500000000000020541 0ustar00zuulzuul00000000000000[run] branch = True source = neutron_dynamic_routing # omit = neutron_dynamic_routing/tests/* [report] ignore_errors = True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/.mailmap0000664000175000017500000000013100000000000020031 0ustar00zuulzuul00000000000000# Format is: # # ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/.pylintrc0000664000175000017500000000661500000000000020272 0ustar00zuulzuul00000000000000# The format of this file isn't really documented; just use --generate-rcfile [MASTER] # Add to the black list. It should be a base name, not a # path. You may set this option multiple times. # ignore=.git,tests [MESSAGES CONTROL] # NOTE(gus): This is a long list. A number of these are important and # should be re-enabled once the offending code is fixed (or marked # with a local disable) disable= # "F" Fatal errors that prevent further processing import-error, # "I" Informational noise locally-disabled, # "E" Error for important programming issues (likely bugs) access-member-before-definition, bad-super-call, maybe-no-member, no-member, no-method-argument, no-self-argument, not-callable, no-value-for-parameter, super-on-old-class, too-few-format-args, # "W" Warnings for stylistic problems or minor programming issues abstract-method, anomalous-backslash-in-string, anomalous-unicode-escape-in-string, arguments-differ, attribute-defined-outside-init, bad-builtin, bad-indentation, broad-except, dangerous-default-value, deprecated-lambda, duplicate-key, expression-not-assigned, fixme, global-statement, global-variable-not-assigned, logging-not-lazy, no-init, non-parent-init-called, pointless-string-statement, protected-access, redefined-builtin, redefined-outer-name, redefine-in-handler, signature-differs, star-args, super-init-not-called, unnecessary-lambda, unnecessary-pass, unpacking-non-sequence, unreachable, unused-argument, unused-import, unused-variable, # TODO(dougwig) - disable nonstandard-exception while we have neutron_lib shims nonstandard-exception, # "C" Coding convention violations bad-continuation, invalid-name, missing-docstring, old-style-class, superfluous-parens, # "R" Refactor recommendations abstract-class-little-used, abstract-class-not-used, duplicate-code, interface-not-implemented, no-self-use, too-few-public-methods, too-many-ancestors, too-many-arguments, too-many-branches, too-many-instance-attributes, too-many-lines, too-many-locals, too-many-public-methods, too-many-return-statements, too-many-statements [BASIC] # Variable names can be 1 to 31 characters long, with lowercase and underscores variable-rgx=[a-z_][a-z0-9_]{0,30}$ # Argument names can be 2 to 31 characters long, with lowercase and underscores argument-rgx=[a-z_][a-z0-9_]{1,30}$ # Method names should be at least 3 characters long # and be lowecased with underscores method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$ # Module names matching neutron-* are ok (files in bin/) module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$ # Don't require docstrings on tests. no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ [FORMAT] # Maximum number of characters on a single line. max-line-length=79 [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. # _ is used by our localization additional-builtins=_ [CLASSES] # List of interface methods to ignore, separated by a comma. ignore-iface-methods= [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules= # should use oslo_serialization.jsonutils json [TYPECHECK] # List of module names for which member attributes should not be checked ignored-modules= [REPORTS] # Tells whether to display a full report or only the messages reports=no ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/.stestr.conf0000664000175000017500000000012500000000000020664 0ustar00zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./neutron_dynamic_routing/tests/unit} top_dir=./ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/.zuul.yaml0000664000175000017500000000262100000000000020357 0ustar00zuulzuul00000000000000- job: name: neutron-dynamic-routing-static parent: neutron-tempest-plugin-dynamic-routing vars: devstack_localrc: BGP_SCHEDULER_DRIVER: neutron_dynamic_routing.services.bgp.scheduler.bgp_dragent_scheduler.StaticScheduler devstack_local_conf: test-config: $TEMPEST_CONFIG: neutron_plugin_options: bgp_schedule_speakers_to_agents: true - job: name: neutron-dynamic-routing-functional parent: neutron-functional vars: project_name: neutron-dynamic-routing-functional tox_envlist: functional # NOTE(amotoki): neutron-functional sets tox_install_siblings to false. # This needs to be true so that neutron from its repo is installed into # tox env. # We need to check tox_install_siblings false is required # in neutron-functional job. tox_install_siblings: true - project: templates: - check-requirements - openstack-cover-jobs-neutron - openstack-lower-constraints-jobs-neutron - openstack-python3-yoga-jobs-neutron - publish-openstack-docs-pti - release-notes-jobs-python3 check: jobs: - neutron-dynamic-routing-functional - neutron-dynamic-routing-static - neutron-tempest-plugin-dynamic-routing gate: jobs: - neutron-dynamic-routing-functional - neutron-tempest-plugin-dynamic-routing ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/AUTHORS0000664000175000017500000002042600000000000017471 0ustar00zuulzuul00000000000000Aaron Rosen Aaron Rosen Abhishek Raut Abhishek Raut Adelina Tuvenie Adit Sarfaty Aggelos Kolaitis Akihiro MOTOKI Akihiro Motoki Akihiro Motoki Andre Pech Andreas Jaeger Andreas Scheuring Angela Smith Ann Kamyshnikova Armando Migliaccio Arvind Somya Assaf Muller AvnishPal Benoît Knecht Bernard Cafarelli Bob Kukura Bob Melander Boden R Brandon Logan Brian Haley Brian Haley Brian Haley Carl Baldwin Carl Baldwin Carol Bouchard Cedric Brandily Chandan Kumar ChangBo Guo(gcb) Chuck Short ChuckC Clark Boylan Claudiu Belu Corey Bryant Cyril Roelandt Dan Florea Dan Wendlandt Darek Smigiel (dasm) Davanum Srinivas David Shaughnessy Dirk Mueller Dongcan Ye Doug Hellmann Doug Wiegley Dr. Jens Harbott Dr. Jens Harbott Elena Ezhova Eugene Nikanorov Fawad Khaliq Federico Ressi Flavio Percoco Gary Kotton Gary Kotton Ghanshyam Mann Guoqiang Ding Hareesh Puthalath He Jie Xu Hemanth Ravi Henry Gessau Henry Gessau Henry Gessau Hirofumi Ichihara Hong Hui Xiao Ian Wienand Ignacio Scopetta Ihar Hrachyshka Irena Berezovsky Isaku Yamahata Ivar Lazzaro Jakub Libosvar James Arendt James E. Blair James E. Blair James Page Jens Harbott Jens Rosenboom Jeremy Stanley John Davidge John Perkins John Schwarz Julien Danjou Kanzhe Jiang Kevin Benton Kevin Benton Kevin L. Mitchell Kobi Samoray Kris Lindgren Kyle Mestery Kyle Mestery LIU Yulong LiuNanke Luke Gorrie Luong Anh Tuan Manu B Mark McClain Mark McClain Martin Hickey Maru Newby Mate Lakat Mathieu Gagné Matt Riedemann Michael Krotscheck Miguel Angel Ajo Miguel Lavalle Mike Bayer Mike Dorman Mike Kolesnik Mohammad Banikazemi Monty Taylor Moshe Levi Motohiro OTSUKA Murali Birru Na Nachi Ueno Nachi Ueno Nader Lahouti Nate Johnston Nguyen Phuong An Nir Magnezi Numan Siddique Nurmatov Mamatisa Oleg Bondarev Omer Anson OpenStack Release Bot Paul Carver Pavel Bondar Praneet Bachheti Pritesh Kothari Ramanjaneya Rich Curran Robert Collins Roey Chen Romil Gupta Russell Bryant Ryan Tidwell Ryan Tidwell Ryan Tidwell Saksham Varma Salvatore Salvatore Orlando Samer Deeb Sandhya Dasu Sascha Peilicke Sascha Peilicke Sean McGinnis Shih-Hao Li Shiv Haris Shweta P Slawek Kaplonski Somik Behera Sreekumar S Sripriya Stephen Ma Sukhdev Sukhdev Kapur Sumit Naiksatam Swaminathan Vasudevan Swapnil Kulkarni (coolsvap) Sylvain Afchain Sławek Kapłoński Terry Wilson Thierry Carrez Thomas Morin Tobias Urdin Tom Holtzen Trinath Somanchi TrinathSomanchi Tuan Do Anh Victor Laza Vieri <15050873171@163.com> Vivekanandan Narasimhan Vlad Gridin YAMAMOTO Takashi YAMAMOTO Takashi Yalei Wang Yong Sheng Gong YuehuiLei Yushiro FURUKAWA Zang MingJie ZhaoBo ZhiQiang Fan abhishek60014726 armando-migliaccio armando-migliaccio asarfaty chenghuiyu elajkat fujioka yuuichi fumihiko kakuma gengchc2 ghanshyam gong yong sheng gongysh gujin ji-xuepeng justin ljj loooosy malei mamtap mark mcclain mathieu-rohon melissaml niusmallnan pengyuesheng qinchunhua qingszhao ronak shanyunfan33 shihanzhang snaiksat steve.ruan sukhdev trinaths unknown vikram.choudhary yuyangbj zengfagao zhangboye zhanghao zhangyanxian zhaojingjing0067370 zhulingjie zoushilin Édouard Thuleau ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/CONTRIBUTING.rst0000664000175000017500000000030700000000000021056 0ustar00zuulzuul00000000000000Please see the Neutron CONTRIBUTING.rst file for how to contribute to neutron-dynamic-routing: `Neutron CONTRIBUTING.rst `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/ChangeLog0000664000175000017500000007055300000000000020201 0ustar00zuulzuul00000000000000CHANGES ======= 20.0.0 ------ * Use TOX\_CONSTRAINTS\_FILE * remove unicode from code * Add a StaticScheduler without automatic scheduling * Fix Mako in lower-constraints * Add Python3 yoga unit tests * Update master for stable/xena * Use payload style callback * Payload changes for FLOATING\_IP events 19.0.0.0rc1 ----------- * use payloads for ROUTER\_INTERFACE events * Use payload for PORT and FLOATING\_IP events * Drop dsvm-functional tox env and related files * Use bgp api-def from neutron-lib * setup.cfg: Replace dashes with underscores * Use py3 as the default runtime for tox * Fix TypeError when formatting BGP IP address * Add Python3 xena unit tests * Update master for stable/wallaby 18.0.0 ------ * Add doc/requirements * Bump SQL Alchemy version to catch ambiguous join fix * Migrate to new engine facade * Fix lower constraints * Fix outerjoin to comply with SQLAlchemy 1.3 strictness * Add Python3 wallaby unit tests * Update master for stable/victoria 17.0.0 ------ * Fix l-c and pep8 testing for ubuntu focal * Update testing doc to use OSC commands * Clean unused job playbooks * functional job: set tox\_install\_siblings to true explicitly * Fix devstack README.rst formatting * Switch from unittest2 compat methods to Python 3.x methods * Clean up workaround in bgp\_dragent\_scheduler * Remove usage of six * Switch to newer openstackdocstheme and reno versions * Remove translation sections from setup.cfg * Use unittest.mock instead of third party mock * Monkey patch original current\_thread \_active * Add Python3 victoria unit tests * Update master for stable/ussuri 16.0.0.0rc1 ----------- * Fix docs publishing * Cleanup py27 support 16.0.0.0b1 ---------- * Switch functional job to Zuulv3 syntax * Drop python 2 support and testing * Remove tempest path from setup.cfg & tox.ini * Remove tempest tests that have moved to neutron-tempest-plugin * Fix resource\_filter.bind method that was changed in neutron * Switch to Ussuri jobs * Update master for stable/train 15.0.0 ------ * PDF documentation build * use payloads for ROUTER\_GATEWAY events * Add Python 3 Train unit tests * bgp: Gracefully handle missing last\_known\_router\_id * Convert CI jobs to python 3 * Replace git.openstack.org URLs with opendev.org URLs * Dropping the py35 testing * Bump neutron version dependency * Make scenario jobs voting * Updated from global requirements * Bump neutron-lib to 1.26.0 * Update hacking version * OpenDev Migration Patch * stop using common\_db\_mixin * Replace openstack.org git:// URLs with https:// * Update master for stable/stein * Ignore DrAgentAssociationError in test\_remove\_add\_speaker\_agent 14.0.0 ------ * Fix installation of docker when docker-engine is not available 14.0.0.0b2 ---------- * Implement DVR-aware fixed IP lookups * Enable MP-BGP capabilities in Ryu BGP driver * add python 3.7 unit test job 14.0.0.0b1 ---------- * stop using common db mixin methods * doc: Add policy reference * Fix gate issues on master * Add agent\_updated to BgpDrAgentNotifyApi notifier * Convert policy.json into policy-in-code * Migrate neutron-dynamic-routing from Ryu to os-ken * neutron-lib version bump to 1.21.0 * use common rpc and exceptions from neutron-lib * use openstack-lower-constraints-jobs-neutron job template * Change openstack-dev to openstack-discuss * load neutron objects using neutron-lib * add local tox targets for pep8 and py3 * use neutron-lib for \_model\_query * use context manager from neutron-lib * Update min tox version to 2.0 * removing older python version 3.4 from setup.cfg * Remove unnecessary mocking of get\_admin\_context * fix tox python3 overrides * Fix \_get\_config\_generator\_config\_definition() typo * Increment versioning with pbr instruction * Use templates for cover and lower-constraints * opt in for neutron-lib consumption patches * add python 3.6 unit test job * Fix unit tests with py3.6 * switch documentation job to new PTI * import zuul job settings from project-config * Update reno for stable/rocky 13.0.0 ------ * update requirements for neutron-lib 1.18.0 * Remove setting of DEVSTACK\_GATE\_EXERCISES * Add release notes link in README * Improve patching of sys.argv 13.0.0.0b3 ---------- * Switch to stestr 13.0.0.0b2 ---------- * Disable subnet CIDR reservation 13.0.0.0b1 ---------- * Fix requirements * use rpc Connection rather than create\_connection * Fix pep8 issues with import order * Enable mutable config in Neutron-dynamic-routing * remove unused plugin.get\_plugin\_name() * Move 4byte\_asn scenario test to basic dir * Skip functional and scenario tests on doc changes * Drop non-existent verbose option * Updated from global requirements * add lower-constraints job * Avoid tox-install.sh * Updated from global requirements * Fix some minor issues in testing doc * Fix TypeError for BgpSpeakerRescheduleError * use common agent topics from neutron-lib * Update docs * Fix failure when adding a speaker to an agent * Zuul: Remove project name * Update reno for stable/queens * Fix unit tests for floating IP creation * Tag the alembic migration revisions for Queens * Fix broken unit tests 12.0.0.0rc1 ----------- * Updated from global requirements * Adding neutron.service\_plugins for "entry\_points" in file "setup.cfg" * The description of the range for local\_as and remote\_as is incorrect in file "base.py" * Switch to neutron-tempest-plugin for tempest tests 12.0.0.0b3 ---------- * Updated from global requirements * Remove redundant gate config in scenario tests * Updated from global requirements * Address breakage from upstream change * Migrate legacy jobs into neutron-dynamic-routing repo 12.0.0.0b2 ---------- * Updated from global requirements * use l3 api def from neutron-lib * Update after agent extension rehome * Update the doc link * Remove setting of version/release from releasenotes * Updated from global requirements * Use agent OVO for bgp\_dragent\_scheduler * Support 4-Byte AS Numbers * Use FAULT\_MAP from neutron-lib * Updated from global requirements * use external net api def from lib * Fix unmatched argument 12.0.0.0b1 ---------- * Use common constants in neutron-lib * Switch to tempest.common.utils.is\_extension\_enabled * Updated from global requirements * Updated from global requirements * Fix checking extension in api test * Stop using is\_agent\_down * Fix unit tests and test configuration * Give docker bridges test-specific names * Reschedule a bgp speaker binded to a down dr-agent * Fix to use "." to source script files * Updated from global requirements * Update reno for stable/pike 11.0.0.0rc1 ----------- * tempest: Avoid using dvr extension * Updated from global requirements 11.0.0.0b3 ---------- * use synchronized decorator from neutron-lib * Add auto-generated config reference * Tempest: Fix DeprecationWarning for tempest.test.attr() * Update the documentation link for doc migration * Enable some off-by-default checks * The parameter "fields" is lack in call to self.\_make\_bgp\_peer\_dict * Updated from global requirements * Use "docker network" in scenario test * rearrange content to fit the new standard layout * Use flake8-import-order plugin * switch from oslosphinx to openstackdocstheme * Enable warning-is-error in doc build * use service type constants from neutron\_lib plugins * Updated from global requirements * devstack: Adapt to lib/neutron * DB Unit tests: BGP gateway netwrok creation made in a designated method * Updated from global requirements * Use Neutron new engine-facade 11.0.0.0b2 ---------- * Updated from global requirements * Allow BGP DB test to run with non ml2 core plugins * Fix exception message for DuplicateBgpPeerIpException * Updated from global requirements * consume neutron-lib callbacks * Switched remaining jobs to ostestr * Remove subunit-trace fork * Enable BGP extension in gate\_hook.sh * Add sudo permission to tempest user in senario test * Revert "Enable BGP extension" * Enable BGP extension * Fix tempest api tests path for tox.ini * Add tempest scenario tests * Disable new N537 hacking check from next neutron-lib * Updated from global requirements 11.0.0.0b1 ---------- * Updated from global requirements * Enable neutron-dynamic-routing scheduler * Delete non-existent file in index.rst * Optimize the link address for fetching git code * Agent common config move * Use neutron-lib's context module * consume ServicePluginBase from neutron-lib * Updated from global requirements * Fix relocated DB models * Updated from global requirements * Use test l3 plugin from neutron * Update reno for stable/ocata 10.0.0 ------ * Fix broken doc links * Switch to decorators.idempotent\_id * Renamed [BGP] config section to [bgp] * tempest plugin: Don't use the same name as neutron 10.0.0.0b3 ---------- * Updated from global requirements * Use neutron-lib portbindings api-def * Updated from global requirements * Don't override min\_l3\_agents\_per\_router in tests * Enable gate\_hook for tempest api test * Gracefully withdraw the floating IP bgp route * Removes unnecessary utf-8 encoding * Updated from global requirements * Replaces uuid.uuid4 with uuidutils.generate\_uuid() 10.0.0.0b2 ---------- * Fix ipv6 transport failure caused by Ryu 4.9 and above * Updated from global requirements * Allow to run multiple kind of tempest tests * Let the bgp\_plugin to query floating IP bgp next\_hop * Use ExtensionDescriptor from neutron-lib * Use DB field sizes instead of \_MAX\_LEN constants * Remove PLURALS * Show team and repo badges on README * Switch to using neutron-lib's model\_base 10.0.0.0b1 ---------- * Updated from global requirements * Switch to using plugins directory in lieu of neutron manager * Fix no attribute error for 'convert\_to\_boolean' * Updated from global requirements * Remove last vestiges of oslo-incubator * fix the comments * Add bits to run neutron-lib periodic test successfully * Updated from global requirements * fix description, tenant to project * fix the comments error * Removing Alembic migration devref document * Updated from global requirements * Replace 'MagicMock' with 'Mock' * Changed the home-page link * Update module path of RPC classes * Drop use of neutron's eventlet utility function * Updated from global requirements * Enable release notes translation * Updated from global requirements * pep8: switched to neutron-lib hacking checks * Update reno for stable/newton * Stop using \_create\_or\_update\_agent * Updated from global requirements 9.0.0.0rc1 ---------- * Tag the alembic migration revisions for Newton * Fixes KeyError while updating bgp peer * Use relocated address scope import * Use model\_base from neutron-lib 9.0.0.0b3 --------- * Updated from global requirements * Update tox.ini for upper constraints * Revert "Fix NoSuchOptError on identity config option lookup" * Fix NoSuchOptError on identity config option lookup * Fixed "tox -e py27" warning message * Add Python 3.5 venv and classifier * Updated from global requirements * Remove temporary local HasProject * Allow to run functional tests * Enable DeprecationWarning in test environments * Updated from global requirements * BGP: exclude legacy fip in DVR fip host routes query * Remove duplicate test l3plugin * Rename DB columns: tenant -> project * Use six.text\_type instead of unicode * Add bgp speaker and route advertisement doc * Clean up API tests for neutron-dynamic-routing * Fix bug for Permission denied * Updated from global requirements 9.0.0.0b2 --------- * Remove unhelpful test of oslo.service ServiceLauncher * remove unused LOG * Document about how to test dynamic routing * Fix unicode bug for password-authenticated BGP peer * Fix \_get\_id\_for * Fix subunit trace help * Address FutureWarning for invalid UUIDs * Fix API document * Trivial documentation change to trigger publish job * Remove check\_i18n files * Fix typo in the installation documentation * Fix exception translation * BGP dynamic routing CLI * Add drivers document for neutron dynamic routing * Add system design for neutron dynamic routing * Fix the policy check for BGP APIs * Fix test-requirements.txt to pull correct version of hacking * Add neutron-dynamic-routing DrAgent & Scheduler document * BGP dynamic routing api documentation 9.0.0.0b1 --------- * Add "neutron-bgp-dragent" to setup.cfg * Fix bgp-speaker-network-remove error * Add devstack for neutron-dynamic-routing stadium project * Fix the issue about BGP dragent reports state failed * Move BGP service plugin, agent, and tests out of Neutron repo * Added OSLO config framework * Documentation * Added alembic DB migration framework * [Trivial] Remove executable privilege of doc/source/conf.py * Prepare initial sandbox for neutron-dynamic-routing * Generated new .gitreview file for neutron-dynamic-routing * API tests: Check extensions with proper functions * Hacking rule to check i18n usage * Make network segment table available for standalone plugin * DSCP QoS rule implementation * register the config generator default hook with the right name * Using LOG.warning replace LOG.warn * Copy tempest.common.tempest\_fixtures in to Neutron * Queries for DVR-aware floating IP next-hop lookups * Add BGP Callback and agent RPC notifcation implementations * Fix tempest lib import in API tests * Add a description field to all standard resources * Add timestamp for neutron core resources * Skip racey BGP tests * Continue the fwaas decoupling and cleanup * Add Queries For BGP Route Lookups * Add tag mechanism for network resources * Moved CORS middleware configuration into oslo-config-generator * BGP: remove unnecessary configuration setting * Add support for QoS for LinuxBridge agent * Add API extension for reporting IP availability usage statistics * macvtap: Macvtap L2 Agent * Switch to using in-tree tempest lib * BGP Dynamic Routing: introduce BgpDriver * API test for get-me-network * BGP Dynamic Routing: introduce BgpDrAgent * macvtap: ML2 mech driver for macvtap network attachments * Open vSwitch conntrack based firewall driver * Make DHCP agent scheduler physical\_network aware * Add the rebinding chance in \_bind\_port\_if\_needed * Remove deprecation warnings * BGP Dynamic Routing: introduce BgpDrScheduler model * Add BGP Dynamic Routing DB Model and Basic CRUD * Remove vpnaas tests from the Neutron API tree * Add the ability to load a set of service plugins on startup * Implement 'get-me-a-network' API building block * Test helpers to facilitate testing BGP dynamic routing * Postpone heavy policy check for ports to later * Delete Tempest fork, import from tempest and tempest\_lib * Add dns\_db to models/head.py * Remove obsolete plugin stuff * External DNS driver reference implementation * Make neutron pecan server an option instead of binary * LBaaS tests code removal * devstack: don't enable qos service with the plugin * Keep py3.X compatibility for urllib * Enable Guru Meditation Reports for other refarch agents * Refactor the subnetpools API tests * Decompose OFAgent mechanism driver from neutron tree completely * move usage\_audit to cmd/eventlet package * Set timetable for removal of oslo.messaging.notify.drivers * Final decomposition of the nuage plugin * Final decomposition of Brocade vendor code * Remove Neutron core static example configuration files - addition * Hyper-V: remove driver from the neutron tree * Remove version from setup.cfg * Automatically generate neutron core configuration files * Freescale ML2 driver code complete decomposition * Remove BigSwitch plugin and driver * Make sure we return unicode strings for process output * Fix Neutron flavor framework * Remove deprecated use\_namespaces option * Remove SysV init script for neutron-server * Final decomposition of opendaylight driver * Remove MidonetInterfaceDriver * Fix notification driver package * Replace subnetpool config options with admin-only API * Decompose ML2 mechanism driver for Mellanox * Decompose ML2 mechanism driver for OVSvApp * Remove IBM SDN-VE left-overs * Add stevedore aliases for interface\_driver configuration * Remove the embrane plugin * Python 3: add classifiers * Remove OneConvergence plugin from the source tree * Decomposition phase2 for MidoNet plugin * Add availability\_zone support base * Add neutron-linuxbridge-cleanup util * Remove OpenContrail plugin from the source tree * Open Mitaka development * Refactoring devstack script * Introduce a separate RPC server * Revert "Revert "Pecan WSGI: prevent plugins from opening AMQP connections"" * Revert "Pecan WSGI: prevent plugins from opening AMQP connections" * Install sriov-agent.ini on 'setup.py install' * SR-IOV: devstack support for SR-IOV agent * Stop device\_owner from being set to 'network:\*' * Final decomposition of Cisco plugin * Remove Cisco Meta and N1KV monolithic plugins * Remove implicit registration of \*-aas service providers * Add Geneve type driver support to ML2 * Removing the SDN-VE monolithic plugin * PLUMgrid plugin decomposition part II * L3 agent changes and reference implementation for IPv6 PD * Decomposition phase2 of NEC plugin * Add empty policy rule to get\_rule\_type action * Remove the ML2 Nuage driver code * Neutron RBAC API and network support * Added initial devstack plugin * Final decomposition of ML2 Cisco UCSM driver * Fix tenant access to qos policies * Final decomposition of ML2 Nexus Driver * NSX: Move DB models as part of core vendor decomposition * Pecan WSGI: prevent plugins from opening AMQP connections * Remove vmware plugin from neutron (etc part) * Support for independent alembic branches in sub-projects * Remove bigswitch mech\_driver entry point definition * Treat sphinx warnings as errors * Final decomposition of the ML2 NCS driver * SR-IOV: Add Agent QoS driver to support bandwidth limit * Replace 'import json' with oslo\_serialization * Fix get\_objects to allow filtering * Moved extensions/qos\_agent.py into extensions/qos.py * Add API tests for non-accessible policies * Cleaned up some TODO comments for feature/qos that do not apply anymore * Added missing [qos] section into neutron.conf * Fix accessing shared policies, add assoc tests * Initial pecan structure * Arista Drivers decomposition part II * Moved out cisco n1kv mech driver and db models * Load the QoS notification driver from the configuration file * Add update tests for policies and rules * Introduce mechanism to determine supported qos rule types for a plugin * Add DB support for resource usage tracking * QoS: Remove type attribute from QoS rules * Decompose Apic ML2 mechanism driver * Base infrastructure for QoS API tests * Metaplugin removal * Remove deprecated OVS and LB plugin DB tables * Handle qos\_policy on network/port create/update * Add address\_scope\_db to neutron/models/head.py * Flavor Framework implementation * Network RBAC DB setup and legacy migration * Add qos section to ovs agent config * Moving out cisco n1kv extensions * Qos Agent Extension * Python3: do not use urllib.urlencode * AgentExtensionsManager and AgentCoreResourceExtension * Support Basic Address Scope CRUD as extensions * QoS: db models and migration rules * Fix Consolidate sriov agent and driver code * QoS service plugin stub * Create the QoS API extension stub * Consolidate sriov agent and driver code * Restructure agent code in preparation for decomp * Updated from global requirements * Add IPset cleanup script * Update version for Liberty * Decompose the NCS ML2 Mechanism Driver * Implement IPAM Driver loader * Fix minor errors in the Vyatta L3 Plugin: * IPAM reference driver * Centralized register\_OVS\_agent in tests * Update build hooks * mlnx MD: mlnx\_direct removal * Extenuate register\_dhcp\_agent code duplication in tests * Add missed actions into policy.json * Disembowel register\_l3\_agent code duplication in tests * ARP spoofing patch: Low level ebtables integration * Block allowed address pairs on other tenants' net * Removed ml2\_conf\_odl.ini config file * Removed ml2\_conf\_odl.ini config file * Open Liberty development * Reorganize unit test tree * Add API tests for subnetpool allocation * tests: don't rely on configuration files outside tests directory * Test to verify shared attribute of network * Add full-stack tests framework * Add L3 router plugin shim for Brocade MLX * Cisco UCS Manager ML2 Mechanism Driver * Cisco Nexus1000V ML2 Mechanism Driver * Ml2 Mechanism Driver for OVSvApp Solution * Add eventlet monkey\_patch helper * Hyper-V Agent decomposition * Basic subnetpool CRUD * Replace keepalived notifier bash script with Python ip monitor * Add ML2 VLAN mechanism driver for Brocade MLX and ICX switches * IBM SDN-VE Plugin decomposition * Brocade Vyatta vrouter shim plugin for vendor decomposition * Add portsecurity extension support * Move mlnx agent to be under ml2/drivers/mlnx * Update api tests from tempest * Remove vendor entry point * Brocade vendor code decomposition from neutron repo * ML2 cisco\_nexus MD: sync config and models with vendor repo * Add rootwrap daemon mode support * NEC plugin code split * ofagent: Have a thin driver module * Initial copy of api tests from tempest * Vendor decomposition to move CSR1000v support to the networking-cisco repo * Decompose the VMware plugin * Remove HyperVNeutronPlugin * ml2 extension driver: more tests, fix data argument inconsistency * ofagent: Vendor code decomposition * Missing entry points for cisco apic topology agents * Fix retrieval of shared firewall\_policies * Added a policy for retrieving the agent hosting a load balancer * test\_l2population: Use a fake mech driver instead of ofagent * Moved several services into neutron.cmd.eventlet * Move NCS mech driver to its new home * Added policy for lbaas v2 agent extension resource * NEC: Merge NEC plugin models into single module * VMware: consolidate NSX models * oslo: migrate to namespace-less import paths * Drop bw compact module for OpenDayLight * Drop deprecated namespace for oslo.rootwrap * Allow port mac\_address to be modified * Drop bin/neutron-rootwrap * Freescale FWaaS Plugin: Update to setup.cfg * Move agent cleanup scripts to cmd module * Service split: cleaned up setup.cfg * Move metadata agent entry to its own file * Break out config and entry point out of l3/agent file * Move postcommit ops out of transaction for bulk * VMWare-NSXv: VMWare NSXv database models * Delete the console scripts for lbaas and vpnaas * Allow to specify IP address of floating ip * Allow setting a tenant router's external IP * Remove NSX 'service' plugin * Backward compatibility for advanced services * Remove mlnx plugin * Move classes out of l3\_agent.py * Services split, pass 2 * Split services code out of Neutron, pass 1 * Remove Python 2.6 classifier * Remove ryu plugin * Add rootwrap filters for ofagent * Remove openvswitch core plugin entry point * Add advsvc role to neutron policy file * remove linuxbridge plugin * Open Kilo development * Fix entrypoint of OneConvergencePlugin plugin * Separate Configuration from Freescale SDN ML2 mechanism Driver * Remove the Cisco Nexus monolithic plugin * Adds ipset support for Security Groups * Add L3 VRRP HA base classes * Supply missing cisco\_cfg\_agent.ini file * Revert "Cisco DFA ML2 Mechanism Driver" * Big Switch: Separate L3 functions into L3 service * Remove reference to cisco\_cfg\_agent.ini from setup.cfg again * Adds router service plugin for CSR1kv * Support for extensions in ML2 * Cisco DFA ML2 Mechanism Driver * Adding mechanism driver in ML2 plugin for Nuage Networks * Remove ovs dependency in embrane plugin * Remove old policies from policy.json * Fix policy rules for adding and removing router interfaces * Fix bigswitch setup.cfg lines * Opencontrail plug-in implementation for core resources * Remove reference to cisco\_cfg\_agent.ini from setup.cfg * Add L3 Scheduler Changes for Distributed Routers * Configuration agent for Cisco devices * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 2 * This patch changes the name of directory from mech\_arista to arista * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 1 * Add rule for updating network's router:external attribute * L2 Model additions to support DVR * Add L3 Extension for Distributed Routers * Freeze models for healing migration * ofagent: move main module from ryu repository * Port to oslo.messaging * Added missing core\_plugins symbolic names * Freescale SDN Mechanism Driver for ML2 Plugin * Remove run-time version checking for openvswitch features * Added missing plugin .ini files to setup.cfg * Disallow regular user to update firewall's shared attribute * Cisco APIC ML2 mechanism driver, part 2 * NSX: get rid of the last Nicira/NVP bits * Perform policy checks only once on list responses * Remove last parts of Quantum compatibility shim * Open Juno development * One Convergence Neutron Plugin l3 ext support * One Convergence Neutron Plugin Implementation * BigSwitch: Add SSL Certificate Validation * Add OpenDaylight ML2 MechanismDriver * Implementaion of Mechanism driver for Brocade VDX cluster of switches * Implement Mellanox ML2 MechanismDriver * Implement OpenFlow Agent mechanism driver * Finish off rebranding of the Nicira NVP plugin * BigSwitch: Add agent to support neutron sec groups * Adds the new IBM SDN-VE plugin * Replace binding:capabilities with binding:vif\_details * Rename Neutron core/service plugins for VMware NSX * Copy cache package from oslo-incubator * Add support to request vnic type on port * Add migration support from agent to NSX dhcp/metadata services * LBaaS: move agent based driver files into a separate dir * Prepare for multiple cisco ML2 mech drivers * Support building wheels (PEP-427) * Use oslo.rootwrap library instead of local copy * Enables BigSwitch/Restproxy ML2 VLAN driver * Base ML2 bulk support on the loaded drivers * Disallow non-admin users update net's shared attribute * Configure plugins by name * Rename nicira configuration elements to match new naming structure * Rename check\_nvp\_config utility tool * Add fwaas\_driver.ini to setup.cfg * Add vpnaas and debug filters to setup.cfg * Open Icehouse development * Allow non-admin user to list service providers * Allow sharing of firewall rules and policies in policy.json * Add l2 population base classes * Install metering\_agent.ini and vpn\_agent.ini * ML2 Mechanism Driver for Cisco Nexus * Reference driver implementation (IPsec) for VPNaaS * Implement ML2 port binding * Arista ML2 Mechanism driver * ML2 Mechanism Driver for Tail-f Network Control System (NCS) * Add Neutron l3 metering agent * Disallow non-admin to specify binding:profile * Add multiple provider network extension * remove binaries under bin * Add metering extension and base class * Remove DHCP lease logic * Add support for the Nexus 1000V into the Cisco Plugin * Firewall as a Service (FWaaS) APIs and DB Model * Service Type Framework refactoring * remove "get\_agents" rule in policy.json * Add agent scheduling for LBaaS namespace agent * Enable policy control over external\_gateway\_info sub-attributes * Add gre tunneling support for the ML2 plugin * Add VXLAN tunneling support for the ML2 plugin * xenapi - rename quantum to neutron * Initial Modular L2 Mechanism Driver implementation * Rename Quantum to Neutron * Remove single-version-externally-managed in setup.cfg * Fix single-version-externally-mananged typo in setup.cfg * Rename agent\_loadbalancer directory to loadbalancer * Add API mac learning extension for NVP * Rename README to README.rst * Add L3 resources to policy.json * Initial Modular L2 plugin implementation * Reduce plugin accesses from policy engine * Move to pbr * Remove calls to policy.check from plugin logic * add db to save host for port * Remove calls to policy.enforce from plugin and db logic * Make the 'admin' role configurable * Enable authZ checks for member actions * Fix typo in policy.json and checks in nicira plugin * Add scheduling feature basing on agent management extension * Use testtools instead of unittest or unittest2 * Agent management extension * Add nvp qos extension * Adds port security api extension and base class * Use babel to generate translation file * API extension and DB support for service types * Add VIF binding extensions * Update policies * Update default policy for add/remove router interface to admin\_or\_owner * Added policy checks for add interface and remove interface * Policies for external networks * Make sure floating IPs + gateways must be on external nets * remove policy check for host\_routes in update\_port * Enable users to list subnets on shared networks * Adds the 'public network' concept to Quantum * Initial V2 implementation of provider extension * Add authZ through incorporation of policy checks * Fix up test running to match jenkins expectation * Add build\_sphinx options * Split out quantum.client and quantum.common * Getting ready for the client split * Pushing initial started code based on Glance project and infrstructure work done by the melange team ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/HACKING.rst0000664000175000017500000000042100000000000020210 0ustar00zuulzuul00000000000000Neutron Dynamic Routing Style Commandments ========================================== Please see the Neutron HACKING.rst file for style commandments for neutron-dynamic-routing: `Neutron HACKING.rst `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/LICENSE0000664000175000017500000002363700000000000017435 0ustar00zuulzuul00000000000000 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. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/MANIFEST.in0000664000175000017500000000020200000000000020145 0ustar00zuulzuul00000000000000include AUTHORS include README.rst include ChangeLog include LICENSE exclude .gitignore exclude .gitreview global-exclude *.pyc ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/PKG-INFO0000664000175000017500000000374000000000000017516 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: neutron-dynamic-routing Version: 20.0.0 Summary: Neutron Dynamic Routing Home-page: https://docs.openstack.org/neutron-dynamic-routing/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/neutron-dynamic-routing.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on This package contains neutron-dynamic-routing code which depends upon neutron and it's related libraries to run. Project Resources ================= The homepage for Neutron is: https://launchpad.net/neutron. Use this site for asking for help, and filing bugs. We use a single launchpad page for all Neutron projects. Code is available on opendev.org at: https://opendev.org/openstack/neutron-dynamic-routing Refer to Neutron documentation for more information: `Neutron README.rst `_ Release notes for the project can be found at: https://docs.openstack.org/releasenotes/neutron-dynamic-routing/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/README.rst0000664000175000017500000000161500000000000020107 0ustar00zuulzuul00000000000000Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/neutron-dynamic-routing.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on This package contains neutron-dynamic-routing code which depends upon neutron and it's related libraries to run. Project Resources ================= The homepage for Neutron is: https://launchpad.net/neutron. Use this site for asking for help, and filing bugs. We use a single launchpad page for all Neutron projects. Code is available on opendev.org at: https://opendev.org/openstack/neutron-dynamic-routing Refer to Neutron documentation for more information: `Neutron README.rst `_ Release notes for the project can be found at: https://docs.openstack.org/releasenotes/neutron-dynamic-routing/ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/TESTING.rst0000664000175000017500000000054500000000000020270 0ustar00zuulzuul00000000000000Testing Neutron Dynamic Routing =============================== Please see the TESTING.rst file for the Neutron project itself. This will have the latest up to date instructions for how to test Neutron, and will be applicable to neutron-dynamic-routing as well: `Neutron TESTING.rst `_ ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/devstack/0000775000175000017500000000000000000000000020221 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/devstack/README.rst0000664000175000017500000000235100000000000021711 0ustar00zuulzuul00000000000000====================== Enabling in Devstack ====================== 1. Download devstack:: git clone https://opendev.org/openstack/devstack.git 2. Add neutron-dynamic-routing to devstack. The minimal set of critical local.conf additions are following:: cd devstack cat << EOF > local.conf > [[local|localrc]] > enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing > EOF 3. run devstack:: ./stack.sh Notes: 1. In the default case, neutron-dynamic-routing is installed in allinone mode. In multiple nodes environment, for controller node:: cd devstack cat << EOF > local.conf > [[local|localrc]] > enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing > DR_MODE=dr_plugin > EOF For the nodes where you want to run dr-agent:: cd devstack cat << EOF > local.conf > [[local|localrc]] > enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing > DR_MODE=dr_agent > EOF 2. In the default case, protocol BGP is enabled for neutron-dynamic-routing. You can change "DR_SUPPORTED_PROTOCOLS" in "devstack/settings" to protocols wanted. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/devstack/lib/0000775000175000017500000000000000000000000020767 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/devstack/lib/dr0000664000175000017500000000562200000000000021324 0ustar00zuulzuul00000000000000function is_protocol_enabled { local enabled=1 local protocol=$1 for temp in $DR_SUPPORTED_PROTOCOLS ;do if [ $protocol == $temp ] ; then enabled=0 fi done return $enabled } ############################## # BGP Section # ############################## function configure_dr_agent_bgp_config { cp $NEUTRON_DYNAMIC_ROUTING_DIR/etc/bgp_dragent.ini.sample $DR_AGENT_BGP_CONF_FILE iniset $DR_AGENT_BGP_CONF_FILE DEFAULT debug $ENABLE_DEBUG_LOG_LEVEL iniset $DR_AGENT_BGP_CONF_FILE bgp bgp_router_id $BGP_ROUTER_ID } function configure_dr_agent_bgp_driver { if [ -z "$BGP_SPEAKER_DRIVER" ] ; then BGP_SPEAKER_DRIVER=$OSKEN_BGP_SPEAKER_DRIVER fi iniset $DR_AGENT_BGP_CONF_FILE bgp bgp_speaker_driver $BGP_SPEAKER_DRIVER } function configure_dr_agent_scheduler_driver { if [ -n "$BGP_SCHEDULER_DRIVER" ] ; then iniset $NEUTRON_CONF DEFAULT bgp_drscheduler_driver $BGP_SCHEDULER_DRIVER fi } ############################# # Stack Install Section # ############################# #This API will be called for phase "install" function dr_install { setup_develop $NEUTRON_DYNAMIC_ROUTING_DIR } ############################# # Stack Post-config Section # ############################# #This API will be called for phase "post-config" function dr_generate_config_files { (cd $NEUTRON_DYNAMIC_ROUTING_DIR && exec ./tools/generate_config_file_samples.sh) } function dr_post_configure { if is_service_enabled q-dr neutron-dr && is_service_enabled q-svc neutron-api; then if is_protocol_enabled BGP; then neutron_service_plugin_class_add $BGP_PLUGIN fi fi if is_service_enabled q-dr-agent neutron-dr-agent; then dr_generate_config_files if is_protocol_enabled BGP; then configure_dr_agent_bgp_config configure_dr_agent_bgp_driver configure_dr_agent_scheduler_driver fi fi } ############################# # Stack Extra Section # ############################# #This API will be called for phase "extra" function start_dr_agent { local process="$DR_AGENT_BINARY --config-file $NEUTRON_CONF " local bgp_parameter if is_protocol_enabled BGP; then bgp_parameter="--config-file $DR_AGENT_BGP_CONF_FILE" fi agent_process=$process$bgp_parameter if is_neutron_legacy_enabled; then if is_service_enabled q-dr-agent; then run_process q-dr-agent "$agent_process" fi else if is_service_enabled neutron-dr-agent; then run_process neutron-dr-agent "$agent_process" fi fi } ############################# # Unstack Section # ############################# #This API will be called for unstack function stop_dr_agent { if is_neutron_legacy_enabled; then stop_process q-dr-agent else stop_process neutron-dr-agent fi } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/devstack/plugin.sh0000664000175000017500000000114000000000000022047 0ustar00zuulzuul00000000000000LIBDIR=$NEUTRON_DYNAMIC_ROUTING_DIR/devstack/lib source $LIBDIR/dr if [[ "$1" == "stack" ]]; then case "$2" in install) echo_summary "Installing neutron-dynamic-routing" dr_install ;; post-config) echo_summary "Configuring neutron-dynamic-routing" dr_post_configure ;; extra) echo_summary "Launching neutron-dynamic-routing agent" start_dr_agent ;; esac elif [[ "$1" == "unstack" ]]; then echo_summary "Uninstalling neutron-dynamic-routing" stop_dr_agent fi ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/devstack/settings0000664000175000017500000000460700000000000022013 0ustar00zuulzuul00000000000000######################### # Devstack Settings # ######################### # Each service you enable has the following meaning: # q-dr - Add this config flag for Openstack Neutron server node # q-dr-agent - Add this config flag indicate that dynamic routing agent # will be running # This can be overridden in the localrc file DR_MODE=${DR_MODE:-allinone} # DR_MODE is used to configure how devstack works with neutron-dynamic-routing. # You can configure it in there ways: # # DR_MODE=allinone # Use this mode if you want to run neutron server and q-dr-agent on same node. # Useful for a single node deployment or on the control node of a multi-node # devstack environment. # # DR_MODE=dr_plugin # Use this to enable dr plugin extension on neutron server # # DR_MODE=dr_agent # Use this for the nodes where you want to run q-dr-agent in a multi-node # devstack environment. case $DR_MODE in allinone) if is_neutron_legacy_enabled; then enable_service q-dr q-dr-agent else enable_service neutron-dr neutron-dr-agent fi ;; dr_plugin) if is_neutron_legacy_enabled; then enable_service q-dr else enable_service neutron-dr fi ;; dr_agent) if is_neutron_legacy_enabled; then enable_service q-dr-agent else enable_service neutron-dr-agent fi ;; esac # DR_SUPPORTED_PROTOCOLS specifies the list of protocols supported # by neutron-dynamic-routing project. ONLY BGP is supported as of now # and it's enabled by default. The protocols may include: "BGP OSPF ISIS RIP". # It can be overridden in the localrc file. DR_SUPPORTED_PROTOCOLS=${DR_SUPPORTED_PROTOCOLS:-"BGP"} ####################### # Binary Settings # ####################### NEUTRON_DYNAMIC_ROUTING_DIR=$DEST/neutron-dynamic-routing DR_AGENT_BINARY=${DR_AGENT_BINARY:-"$NEUTRON_BIN_DIR/neutron-bgp-dragent"} ################################ # Protocol Config Settings # ################################ ########### # BGP # ########### DR_AGENT_BGP_CONF_FILE=${DR_AGENT_BGP_CONF_FILE:-"$NEUTRON_CONF_DIR/bgp_dragent.ini"} BGP_ROUTER_ID=${BGP_ROUTER_ID:-"127.0.0.1"} BGP_PLUGIN=${BGP_PLUGIN:-"neutron_dynamic_routing.services.bgp.bgp_plugin.BgpPlugin"} OSKEN_BGP_SPEAKER_DRIVER="neutron_dynamic_routing.services.bgp.agent.driver.os_ken.driver.OsKenBgpDriver" ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/0000775000175000017500000000000000000000000017162 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/requirements.txt0000664000175000017500000000025400000000000022447 0ustar00zuulzuul00000000000000reno>=3.1.0 # Apache-2.0 sphinx>=2.0.0,!=2.1.0 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD sphinxcontrib-svg2pdfconverter>=0.1.0 # BSD openstackdocstheme>=2.2.1 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/source/0000775000175000017500000000000000000000000020462 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/source/_static/0000775000175000017500000000000000000000000022110 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/_static/.placeholder0000664000175000017500000000000000000000000024361 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/source/admin/0000775000175000017500000000000000000000000021552 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/admin/agent-scheduler.rst0000664000175000017500000001344500000000000025365 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ===== Agent ===== Neutron-dynamic-routing implements a new agent named "DRAgent". The agent talks to the neutron-dynamic-routing plugin which resides in the neutron server to get routing entity configuration. DRAgent interacts with the back-end driver to realize the required dynamic routing protocol functionality. For details, please refer to the system design document :doc:`system-design` .. note:: One DRAgent can support multiple drivers but currently ONLY os-ken is integrated successfully. Scheduler ========= Neutron-dynamic-routing scheduler, schedules a routing entity to a proper DRAgent. BGP Scheduler ------------- BGP Speaker and DRAgent has 1:N association which means one BGP speaker can be scheduled on multiple DRAgents. There are different options for the scheduling algorithm to be used, these can be selected via the ``bgp_drscheduler_driver`` configuration option. StaticScheduler ~~~~~~~~~~~~~~~ This is the most simple option, which does no automatic scheduling at all. Instead it relies on API requests to explicitly associate BGP speaker with DRAgents and to disassociate them again. Sample configuration:: bgp_drscheduler_driver = neutron_dynamic_routing.services.bgp.scheduler.bgp_dragent_scheduler.StaticScheduler Here is an example to associate/disassociate a BGP Speaker to/from a DRAgent. .. TODO(frickler): update the examples to use OSC :: (neutron) bgp-speaker-list +--------------------------------------+------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------+----------+------------+ | 0967eb04-59e5-4ca6-a0b0-d584d8d4a132 | bgp2 | 200 | 4 | | a73432c3-a3fc-4b1e-9be2-6c32a61df579 | bgp1 | 100 | 4 | +--------------------------------------+------+----------+------------+ (neutron) agent-list +--------------------------------------+---------------------------+---------------------+-------------------+-------+----------------+---------------------------+ | id | agent_type | host | availability_zone | alive | admin_state_up | binary | +--------------------------------------+---------------------------+---------------------+-------------------+-------+----------------+---------------------------+ | 0c21a829-4fd6-4375-8e65-36db4dc434ac | DHCP agent | steve-devstack-test | nova | :-) | True | neutron-dhcp-agent | | 0f9d6886-910d-4af4-b248-673b22eb9e78 | Metadata agent | steve-devstack-test | | :-) | True | neutron-metadata-agent | | 5908a304-b9d9-4e8c-a0af-96a066a7c87e | Open vSwitch agent | steve-devstack-test | | :-) | True | neutron-openvswitch-agent | | ae74e375-6a75-4ebe-b85c-6628d2baf02f | L3 agent | steve-devstack-test | nova | :-) | True | neutron-l3-agent | | dbd9900e-9d16-444d-afc4-8d0035df5ed5 | BGP dynamic routing agent | steve-devstack-test | | :-) | True | neutron-bgp-dragent | +--------------------------------------+---------------------------+---------------------+-------------------+-------+----------------+---------------------------+ (neutron) bgp-dragent-speaker-add dbd9900e-9d16-444d-afc4-8d0035df5ed5 bgp1 Associated BGP speaker bgp1 to the Dynamic Routing agent. (neutron) bgp-speaker-list-on-dragent dbd9900e-9d16-444d-afc4-8d0035df5ed5 +--------------------------------------+------+----------+------------+ | id | name | local_as | ip_version | +--------------------------------------+------+----------+------------+ | a73432c3-a3fc-4b1e-9be2-6c32a61df579 | bgp1 | 100 | 4 | +--------------------------------------+------+----------+------------+ (neutron) bgp-dragent-speaker-remove dbd9900e-9d16-444d-afc4-8d0035df5ed5 bgp1 Disassociated BGP speaker bgp1 from the Dynamic Routing agent. (neutron) bgp-speaker-list-on-dragent dbd9900e-9d16-444d-afc4-8d0035df5ed5 (neutron) ReST API's for neutron-dynamic-routing scheduler are defined as part of the `Neutron API reference`_. .. _Neutron API reference: https://docs.openstack.org/api-ref/network/#bgp-dynamic-routing ChanceScheduler ~~~~~~~~~~~~~~~ This is the default option. It will automatically schedule newly created BGP speakers to one of the active DRAgents. When a DRAgent goes down, the BGP speaker will be disassociated from it and an attempt is made to schedule it to a different agent. Note that this action will override any manual associations that have been performed via the API, so you will want to use this scheduler only in very basic deployments. Sample configuration:: bgp_drscheduler_driver = neutron_dynamic_routing.services.bgp.scheduler.bgp_dragent_scheduler.ChanceScheduler ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/admin/bgp-speaker.rst0000664000175000017500000001673500000000000024520 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) BGP Speaker =========== BGP Speaker acts as a route server using BGP routing protocol. It advertises routes to the BGP peers which are added to the BGP Speaker. Now there is a framework that allows different `BGP drivers <../contributor/dragent-drivers.html>`_ to be plugged into a `dynamic routing agent <./agent-scheduler.html>`_. Currently, BGP Speaker only advertises routes for a network to which it is associated. A BGP Speaker requires association with a "gateway" network to determine eligible routes. In Neutron, a "gateway" network connects Neutron routers to the upstream routers. An external network is best for being used as a gateway network. The association builds a list of all virtual routers with gateways on provider and self-service networks within the same address scope. Hence, the BGP speaker advertises self-service network prefixes with the corresponding router as the next-hop IP address. For details refer to `Route advertisement <./route-advertisement.html>`_. Address Scopes -------------- `Address scopes `_ provide flexible control as well as decoupling of address overlap from tenancy, so this kind control can provide a routable domain, the domain has itself route and no overlap address, it means an address scope define "a L3 routing domain". BGP Speaker will associate the external networks and advertise the tenant's networks routes. Those networks should reside in the same address scope. Neutron can route the tenant network directly without NAT. Then Neutron can host globally routable IPv4 and IPv6 tenant networks. For determining which tenant networks prefixes should be advertised, Neutron will identify all routers with gateway ports on the network which had been bounded with BGP Speaker, check the address scope of the subnets on all connected networks, then begin advertising nexthops for all tenant networks to routers on the bound network. BGP Peer -------- BGP peer defined in Neutron represents real BGP infrastructure such as routers, route reflectors and route servers. When a BGP peer is defined and associated with a BGP Speaker, Neutron will attempt to open a BGP peering session with the mentioned remote peer. It is this session, using which Neutron announces it's routes. How to configure a remote peer ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A remote peer can be real or virtual e.g. vRouters or real routers. The remote peer should be configured to handle peering with Neutron in passive mode. The peer needs to waits for the Neutron dynamic routing agent to initiate the peering session. Also, the remote peer can be configured in active mode, but it still can speak BGP until the complete initialization of BGP Speaker running on Neutron dynamic routing agent. Configuring BGP Speaker: One needs to ensure below points for setting a BGP connection. * Host running Neutron dynamic agent MUST connect to the external router. * BGP configuration on the router should be proper. ``bgp router-id XX.XX.XX.XX`` This must be an IP address, the unique identifier of BGP routers actually and can be virtual. If one doesn't configure the router-id, it will be selected automatically as the highest IP address configured for the local interfaces. Just a suggestion, please make sure that it is the same as the ``peer_ip`` which you configure in Neutron for distinguishing easily. ``local_as`` Autonomous System number can be same or different from the AS_id of external BGP router. AS_id will be same for iBGP and different for eBGP sessions. Setting BGP peer: :: neighbor A.B.C.D remote-as AS_ID A.B.C.D is the host IP which run Neutron dynamic routing agent. A Sample Quagga router configuration file forming BGP peering with Neutron: :: ! password zebra log file /var/log/quagga/bgpd.log ! debug bgp events debug bgp keepalives debug bgp updates debug bgp fsm debug bgp filters ! bgp multiple-instance ! router bgp view test-as bgp router-id neighbor remote-as neighbor passive ! line vty ! BGP Speaker Architecture ------------------------ Dynamic routing project saves BGP Speaker configuration as per the defined `data model `_. and pass on the configuration request to the dynamic routing agent for further processing. The implementation of a BGP Speaker is driver specific. During the driver interface initialization process, needed configurations are read from the configuration file and BGP Speaker object instance is created. For details refer to `BGP drivers <../contributor/dragent-drivers.html>`_. BGP Speaker Life Cycle ~~~~~~~~~~~~~~~~~~~~~~ Now we support OsKenBgpDriver, BGP Speaker will be processed by Dragent. When associating a BGP Speaker with an active Dragent, the plugin will send an RPC message to the agent for calling driver in order to create a BGP Speaker instance. In OsKenBgpDriver, the created instance ``BGP Speaker`` will setup by router-id and ASN, then os-ken will setup new context with speaker configuration and listeners which monitor whether the related peers are alive. Then the following operation could be done. * Add peers to BGP Speaker When BGP Speaker is not associated with an active Dragent, there is no real speaker instance, so it will be still the db operation until the speaker is associated with dragent, and all the peers connection before will be setup by ``BGP Speaker`` creation. If add peers into speaker which is running, Dragent will call driver to add peer dynamically. For OsKenBgpDriver, it will register a new neighbor based on your peer configuration and try to establish a session with the peer. * Delete peers from BGP Speaker The same logic with below, but it is reverse. If you don't want use the specific BGP Speaker anymore, you can use CLI: ``neutron bgp-speaker-delete `` BGP Plugin will find all the associated Dragent and send RPC ``bgp_speaker_remove_end`` to make the Dragents to clean the ``BGP Speaker`` instances. This is the same with CLI: ``neutron bgp-dragent-speaker-remove `` BGP Plugin just send rpc ``bgp_speaker_remove_end`` to the specific Dragent. Advertisement ~~~~~~~~~~~~~ For details refer to `Route Advertisement <./route-advertisement.html>`_. How to work ----------- For details refer to `Testing <../contributor/testing.html>`_. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/admin/index.rst0000664000175000017500000000211200000000000023407 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ==================== Administration Guide ==================== .. toctree:: :maxdepth: 2 system-design bgp-speaker route-advertisement agent-scheduler ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/admin/route-advertisement.rst0000664000175000017500000001262300000000000026316 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Route Advertisement =================== BGP --- This page discusses the behavior of BGP dynamic routing about how to advertise routes and show the routes details in the project. BGP dynamic routing could advertise 3 classes of routes: * Host routes for floating IP addresses hosted on non-DVR routers, as floatingip address set on the router namespace, it knows how to route the message to the correct way, so the next-hop should be the IP address of router gateway port. * Host routes for floating IP addresses hosted on DVR routers. With DVR-enabled routers, the floating IP can be reached directly on the compute node hosting a given instance. As such, host routes for the floating IP address should advertise the FIP agent gateway on the compute node as the next-hop instead of the centralized router. This will keep inbound floating IP traffic from encountering the bottleneck of the centralized router. * Prefix routes for directly routable tenant networks with address scopes, the nexthop is the centralized router, the same for DVR and CVR. BGP dynamic routing could advertise tenant network prefixes to physical network devices(routers which support BGP protocol), called this ``Prefixes advertisement``. When distributed virtual routing (DVR) is enabled on a router, next-hops for floating IP's and fixed IP's are not advertised as being at the centralized router. Host routes with the next-hop set to the appropriate compute node are advertised. Logical Model ~~~~~~~~~~~~~ :: +--------+ 1 N +---------------------+ | Router |---------| BgpAdvertisedRoute | +--------+ +---------------------+ | N | | 1 +---------+ N N +------------+ N N +---------+ | BgpPeer |-----------| BgpSpeaker |-----------| Network | +---------+ +------------+ +---------+ | N | | 1 +--------------+ | AddressScope | +--------------+ .. note:: A BGP Speaker only supports one address family to speak BGP. A dual-stack IPv4 and IPv6 network needs two BGP Speakers to advertise the routes with BGP, one for IPv4 and the other for IPv6. So A network can have N number of BGP Speakers bound to it. BgpAdvertisedRoute represents derived data. As the number of BgpAdvertisedRoutes can be quite large, storing in a database table is not feasible. BgpAdvertisedRoute information can be derived by joining data already available in the Neutron database. And now BGP dynamic routing project process the Bgpadvertiseroutes which should be advertised to external Router is basing on the exist Neutron DB tables. Neutron looks on each of the gateway network for any routers with a gateway port on that network. For each router identified, Neutron locates each floating IP and tenant network accessible through the router gateway port. Neutron then advertises each floating IP and tenant network with the IP address of the router gateway port as the next hop. When BGP Plugin is started, it will register callbacks. All callbacks are used for processing Floating IP, Router Interface and Router Gateway creation or update, this functions listen the events of these resources for calling Dragent to change the advertisement routes. Now we just focus on the resources which may cause route change, the following callbacks does this work. * floatingip_update_callback This function listens to the Floating IP's AFTER_UPDATE event, it judges whether the associated router is changed, and changes the advertisement routes and nexthop based on that. * router_interface_callback This function listens to the tenants' network routes change, it listens to AFTER_CREATE and AFTER_DELETE events of Router Interface resource. It calls Dragent to advertise or stop the prefix routes after a interface attach into a router. * router_gateway_callback This function listens to the router gateway port creation or deletion. It also focuses on tenants' network routes change. You could get the advertisement routes of specific BGP Speaker like: ``neutron bgp-speaker-advertiseroute-list `` It does a complicated db query to generate the list of advertised routes. For more details refer to `route advertisement db lookup `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/admin/system-design.rst0000664000175000017500000001321200000000000025076 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) System Design ============= Introduction ------------ Neutron dynamic routing enables advertisement of self-service (private) network prefixes to physical network devices that support dynamic routing protocols such as routers, thus removing the conventional dependency on static routes. It advertises three classes of routes: * Host routes for floating IP addresses hosted on non-DVR routers, the nexthop is the centralized router. * Host routes for floating IP addresses hosted on DVR routers, the nexthop is the appropriate compute node. * Prefix routes for directly routable tenant networks with address scopes, the nexthop is the centralized router, the same for DVR and CVR. For details refer to `Route Advertisement <./route-advertisement.html>`_. Neutron dynamic routing consists of `service plug-in `_ and agent. The service plug-in implements the Networking service extension and the agent manages dynamic routing protocol peering sessions. The plug-in communicates with the agent through RPC. Architecture ------------ The following figure shows the architecture of this feature:: Neutron dynamic Routing System Architecture +---------------------------------------------------------------+ | Dynamic Routing plug-in | | +---------------------------------------------------------+ | | | Dynamic Routing API/Model | | | +---------------------------------------------------------+ | | | Dynamic Routing Agent Scheduler | | | +---------------------------------------------------------+ | | | | +------------------------------|--------------------------------+ | | +-----------+ | RPC | +-----------+ | | +----------------------|-------------------------+ | | | | +---------------------------+ +---------------------------+ | Dynamic Routing Agent1 | | Dynamic Routing Agent2 | | | | | | +---------------------+ | | +---------------------+ | | | Driver Manager | | | | Driver Manager | | | +---------------------+ | | +---------------------+ | | | Common Driver API | | | | Common Driver API | | | +---------------------+ | | +---------------------+ | | | | | | | | +---------+-----------+ | | +---------+-----------+ | | | os-ken | Other | | | | os-ken | Other | | | | Driver | Drivers | | | | Driver | Drivers | | | +---------+-----------+ | | +---------+-----------+ | | | | | +---------------------------+ +---------------------------+ Dynamic Routing Plug-in ~~~~~~~~~~~~~~~~~~~~~~~ Using dynamic routing plugin one can enable/disable the support of dynamic routing protocols in neutron. Dynamic Routing API ~~~~~~~~~~~~~~~~~~~ Dynamic routing API provides APIs to configure dynamic routing. API's for below mentioned dynamic protocols are supported. BGP +++ Three kinds of APIs are available for BGP functionality.For details refer to the `API document <../reference/index.html>`_. * BGP Speaker APIs to advertise Neutron routes outside the Openstack network. * BGP Peer APIs to form peers with the remote routers. * BGP DRAgentScheduler APIs to schedule BGP Speaker(s) to one or more hosts running the dynamic routing agent. .. note:: BGP is the only dynamic routing protocol currently supported. Dynamic Routing Model ~~~~~~~~~~~~~~~~~~~~~ Dynamic routing model maintains the database and communicates with the dynamic routing agent. Dynamic Routing Agent Scheduler ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dynamic routing agent scheduler, is responsible for scheduling a routing entity. For details refer to `Agent Scheduler <./agent-scheduler.html>`_. Dynamic Routing Agent (DR Agent) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Dynamic routing can reside on hosts with or without other Networking service agents. It manages and configures different dynamic routing stack through `Common Driver API <../contributor/dragent-drivers.html>`_. .. note:: Currently, only integration with `os-ken `_ is supported. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/source/cli/0000775000175000017500000000000000000000000021231 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/cli/bgp-peer.rst0000664000175000017500000001063300000000000023467 0ustar00zuulzuul00000000000000======== BGP Peer ======== BGP Peer Create --------------- .. code-block:: console usage: neutron bgp-peer-create [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [--tenant-id TENANT_ID] --peer-ip PEER_IP_ADDRESS --remote-as PEER_REMOTE_AS [--auth-type PEER_AUTH_TYPE] [--password AUTH_PASSWORD] NAME Create a BGP Peer. **Positional arguments:** ``NAME`` Name of the BGP peer to create ``--peer-ip PEER_IP_ADDRESS`` Peer IP address. ``--remote-as PEER_REMOTE_AS`` Peer AS number. (Integer in [1, 65535] is allowed.) **Optional arguments:** ``-h, --help`` show this help message and exit ``--auth-type PEER_AUTH_TYPE`` Authentication algorithm. Supported algorithms: none(default), md5 ``--password AUTH_PASSWORD`` Authentication password. BGP Peer List ------------- .. code-block:: console usage: neutron bgp-peer-list [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] [-P SIZE] [--sort-key FIELD] [--sort-dir {asc,desc}] List BGP peers. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Peer Show ------------- .. code-block:: console usage: neutron bgp-peer-show [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [-D] [-F FIELD] BGP_PEER Show information of a given BGP peer. **Positional arguments:** ``BGP_PEER`` ID or name of the BGP peer to look up. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Peer Delete --------------- .. code-block:: console usage: neutron bgp-peer-delete [-h] [--request-format {json}] BGP_PEER Delete a BGP peer. **Positional arguments:** ``BGP_PEER`` ID or name of the BGP peer to delete. **Optional arguments:** ``-h, --help`` show this help message and exit BGP Peer Update --------------- .. code-block:: console usage: neutron bgp-peer-update [-h] [--request-format {json}] [--name NAME] [--password AUTH_PASSWORD] BGP_PEER Update BGP Peer's information. **Positional arguments:** ``BGP_PEER`` ID or name of the BGP peer to update. **Optional arguments:** ``-h, --help`` show this help message and exit ``--name NAME`` Updated name of the BGP peer. ``--password AUTH_PASSWORD`` Updated authentication password. Add Peer to BGP Speaker ----------------------- .. code-block:: console usage: neutron bgp-speaker-peer-add [-h] [--request-format {json}] BGP_SPEAKER BGP_PEER Add a peer to the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``BGP_PEER`` ID or name of the BGP peer to add. **Optional arguments:** ``-h, --help`` show this help message and exit Delete Peer from BGP Speaker ---------------------------- .. code-block:: console usage: neutron bgp-speaker-peer-remove [-h] [--request-format {json}] BGP_SPEAKER BGP_PEER Remove a peer from the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``BGP_PEER`` ID or name of the BGP peer to remove. **Optional arguments:** ``-h, --help`` show this help message and exit ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/cli/bgp-speaker.rst0000664000175000017500000001525700000000000024175 0ustar00zuulzuul00000000000000=========== BGP Speaker =========== BGP Speaker Create ------------------ .. code-block:: console usage: neutron bgp-speaker-create [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [--tenant-id TENANT_ID] --local-as LOCAL_AS [--ip-version {4,6}] [--advertise-floating-ip-host-routes {True,False}] [--advertise-tenant-networks {True,False}] NAME Create a BGP Speaker with a specified NAME. **Positional arguments:** ``NAME`` Name of the BGP speaker to create. **Optional arguments:** ``-h, --help`` show this help message and exit ``--local-as LOCAL_AS`` Local AS number. (Integer in [1, 65535] is allowed.) ``--ip-version {4,6}`` IP version for the BGP speaker (default is 4) ``--advertise-floating-ip-host-routes {True,False}`` Whether to enable or disable the advertisement of floating-ip host routes by the BGP speaker. By default floating ip host routes will be advertised by the BGP speaker. ``--advertise-tenant-networks {True,False}`` Whether to enable or disable the advertisement of tenant network routes by the BGP speaker. By default tenant network routes will be advertised by the BGP speaker. BGP Speaker List ---------------- .. code-block:: console usage: neutron bgp-speaker-list [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] [-P SIZE] [--sort-key FIELD] [--sort-dir {asc,desc}] List BGP speakers. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Speaker Show ---------------- .. code-block:: console usage: neutron bgp-speaker-show [-h] [-f {html,json,json,shell,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--prefix PREFIX] [--request-format {json}] [-D] [-F FIELD] BGP_SPEAKER Show information of a given BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker to look up. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. BGP Speaker Delete ------------------ .. code-block:: console usage: neutron bgp-speaker-delete [-h] [--request-format {json}] BGP_SPEAKER Delete a BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker to delete. **Optional arguments:** ``-h, --help`` show this help message and exit BGP Speaker Update ------------------ .. code-block:: console usage: neutron bgp-speaker-update [-h] [--request-format {json}] [--name NAME] [--advertise-floating-ip-host-routes {True,False}] [--advertise-tenant-networks {True,False}] BGP_SPEAKER Update BGP Speaker's information. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker to update. **Optional arguments:** ``-h, --help`` show this help message and exit ``--name NAME`` Name of the BGP speaker to update. ``--advertise-floating-ip-host-routes {True,False}`` Whether to enable or disable the advertisement of floating-ip host routes by the BGP speaker. By default floating ip host routes will be advertised by the BGP speaker. ``--advertise-tenant-networks {True,False}`` Whether to enable or disable the advertisement of tenant network routes by the BGP speaker. By default tenant network routes will be advertised by the BGP speaker. Add Network to BGP Speaker --------------------------- .. code-block:: console usage: neutron bgp-speaker-network-add [-h] [--request-format {json}] BGP_SPEAKER NETWORK Add a network to the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``NETWORK`` ID or name of the network to add. **Optional arguments:** ``-h, --help`` show this help message and exit Delete Network from BGP Speaker ------------------------------- .. code-block:: console usage: neutron bgp-speaker-network-remove [-h] [--request-format {json}] BGP_SPEAKER NETWORK Remove a network from the BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. ``NETWORK`` ID or name of the network to remove. **Optional arguments:** ``-h, --help`` show this help message and exit BGP Advertised Routes List -------------------------- .. code-block:: console usage: neutron bgp-speaker-advertiseroute-list [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] [-P SIZE] [--sort-key FIELD] [--sort-dir {asc,desc}] BGP_SPEAKER List routes advertised by a given BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/cli/dynamic-routing-agent.rst0000664000175000017500000000641000000000000026171 0ustar00zuulzuul00000000000000===================== Dynamic Routing Agent ===================== Add BGP Speaker to Dynamic Routing Agent ---------------------------------------- .. code-block:: console usage: neutron bgp-dragent-speaker-add [-h] [--request-format {json}] BGP_DRAGENT_ID BGP_SPEAKER Add a BGP speaker to a Dynamic Routing agent. **Positional arguments:** ``BGP_DRAGENT_ID`` ID of the Dynamic Routing agent. ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit Delete BGP Speaker from Dynamic Routing Agent --------------------------------------------- .. code-block:: console usage: neutron bgp-dragent-speaker-remove [-h] [--request-format {json}] BGP_DRAGENT_ID BGP_SPEAKER Removes a BGP speaker from a Dynamic Routing agent. **Positional arguments:** ``BGP_DRAGENT_ID`` ID of the Dynamic Routing agent. ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit List BGP Speakers hosted by a Dynamic Routing Agent --------------------------------------------------- .. code-block:: console usage: neutron bgp-speaker-list-on-dragent [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] BGP_DRAGENT_ID List BGP speakers hosted by a Dynamic Routing agent. **Positional arguments:** ``BGP_DRAGENT_ID`` ID of the Dynamic Routing agent. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. List Dynamic Routing Agents Hosting a BGP Speaker ------------------------------------------------- .. code-block:: console usage: neutron bgp-dragent-list-hosting-speaker [-h] [-f {csv,html,json,json,table,value,yaml,yaml}] [-c COLUMN] [--max-width ] [--noindent] [--quote {all,minimal,none,nonnumeric}] [--request-format {json}] [-D] [-F FIELD] BGP_SPEAKER List Dynamic Routing agents hosting a BGP speaker. **Positional arguments:** ``BGP_SPEAKER`` ID or name of the BGP speaker. **Optional arguments:** ``-h, --help`` show this help message and exit ``-D, --show-details`` Show detailed information. ``-F FIELD, --field FIELD`` Specify the field(s) to be returned by server. You can repeat this option. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/cli/index.rst0000664000175000017500000000255400000000000023100 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Command-Line Interface ====================== Neutron client has provided the command-line interfaces (CLI) to realize dynamic routing services supported by neutron-dynamic-routing project. Current implementation only supports the command line interfaces for BGP functionality. For query on what specific :command:`neutron bgp` commands are supported, enter: .. code-block:: console $ neutron help | grep bgp .. toctree:: :maxdepth: 2 :glob: * ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/conf.py0000664000175000017500000001011600000000000021760 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', #'sphinx.ext.intersphinx', 'openstackdocstheme', 'oslo_config.sphinxext', 'oslo_config.sphinxconfiggen', 'oslo_policy.sphinxext', 'oslo_policy.sphinxpolicygen', 'sphinxcontrib.rsvgconverter', ] # openstackdocstheme options openstackdocs_repo_name = 'openstack/neutron-dynamic-routing' openstackdocs_pdf_link = True openstackdocs_auto_name = False openstackdocs_bug_project = 'neutron' openstackdocs_bug_tag = 'doc' # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'neutron-dynamic-routing' copyright = '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 = 'native' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' html_theme = 'openstackdocs' html_static_path = ['_static'] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # -- Options for LaTeX output ------------------------------------------------- # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual], torctree_only). latex_documents = [ ('index', 'doc-%s.tex' % project, '%s Documentation' % project, 'OpenStack Foundation', 'manual', # Specify toctree_only=True for a better document structure of # the generated PDF file. True), ] # Disable usage of xindy https://bugzilla.redhat.com/show_bug.cgi?id=1643664 latex_use_xindy = False latex_domain_indices = False latex_elements = { 'makeindex': '', 'printindex': '', 'preamble': r'\setcounter{tocdepth}{3}', } # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'bgp_dragent.ini', ] def _get_config_generator_config_definition(conf): config_file_path = '../../etc/oslo-config-generator/%s' % conf # oslo_config.sphinxconfiggen appends '.conf.sample' to the filename, # strip file extentension (.conf or .ini). output_file_path = '_static/config_samples/%s' % conf.rsplit('.', 1)[0] return (config_file_path, output_file_path) config_generator_config_file = [ _get_config_generator_config_definition(conf) for conf in _config_generator_config_files ] # -- Options for oslo_policy.sphinxpolicygen --------------------------------- policy_generator_config_file = '../../etc/oslo-policy-generator/policy.conf' sample_policy_basename = '_static/neutron-dynamic-routing' ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/source/configuration/0000775000175000017500000000000000000000000023331 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/configuration/bgp_dragent.rst0000664000175000017500000000017600000000000026343 0ustar00zuulzuul00000000000000=============== bgp_dragent.ini =============== .. show-options:: :config-file: etc/oslo-config-generator/bgp_dragent.ini ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/configuration/index.rst0000664000175000017500000000142100000000000025170 0ustar00zuulzuul00000000000000=================== Configuration Guide =================== Configuration ------------- This section provides a list of all possible options for each configuration file. neutron-dynamic-routing uses the following configuration files for its various services. .. toctree:: :maxdepth: 1 bgp_dragent The following are sample configuration files for neutron-dynamic-routing. These are generated from code and reflect the current state of code in the neutron-dynamic-routing repository. .. toctree:: :glob: :maxdepth: 1 samples/* Policy ------ neutron-dynamic-routing, like most OpenStack projects, uses a policy language to restrict permissions on REST API actions. .. toctree:: :maxdepth: 1 Policy Reference Sample Policy File ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/configuration/policy-sample.rst0000664000175000017500000000117400000000000026644 0ustar00zuulzuul00000000000000========================================== Sample neutron-dynamic-routing Policy File ========================================== The following is a sample neutron-dynamic-routing policy file for adaptation and use. The sample policy can also be viewed in :download:`file form `. .. important:: The sample policy file is auto-generated from neutron-dynamic-routing when this documentation is built. You must ensure your version of neutron-dynamic-routing matches the version of this documentation. .. literalinclude:: /_static/neutron-dynamic-routing.policy.yaml.sample ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/configuration/policy.rst0000664000175000017500000000052200000000000025361 0ustar00zuulzuul00000000000000================================ neutron-dynamic-routing policies ================================ The following is an overview of all available policies in neutron-dynamic-routing. For a sample configuration file, refer to :doc:`/configuration/policy-sample`. .. show-policy:: :config-file: etc/oslo-policy-generator/policy.conf ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3886602 neutron-dynamic-routing-20.0.0/doc/source/configuration/samples/0000775000175000017500000000000000000000000024775 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/configuration/samples/bgp_dragent.rst0000664000175000017500000000041200000000000030000 0ustar00zuulzuul00000000000000====================== Sample bgp_dragent.ini ====================== This sample configuration can also be viewed in `the raw format <../../_static/config_samples/bgp_dragent.conf.sample>`_. .. literalinclude:: ../../_static/config_samples/bgp_dragent.conf.sample ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/doc/source/contributor/0000775000175000017500000000000000000000000023034 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000175300000000000026303 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ============ Contributing ============ .. include:: ../../../CONTRIBUTING.rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/contributor/dragent-drivers.rst0000664000175000017500000001113100000000000026663 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) DRAgent Drivers =============== Introduction ------------ The Neutron dynamic routing drivers are used to support different dynamic routing protocol stacks which implement the dynamic routing functionality. As shown in the following figure, the drivers are managed by `DRAgent <./agent-scheduler.html>`_ through a "Driver Manager" which provides consistent APIs to realize the functionality of a dynamic routing protocol:: Neutron Dynamic Routing Drivers +-------------------------------+ | DRAgent | | | | +-------------------------+ | | | Driver Manager | | | +-------------------------+ | | | Common Driver API | | | +-------------------------+ | | | | | | | | +------------+------------+ | | | os-ken | Other | | | | Driver | Drivers | | | +------------+------------+ | | | +-------------------------------+ .. note:: Currently only the integration with os-ken is supported BGP is the only protocol supported. Configuration ------------- Driver configurations are done in a separate configuration file. BGP Driver ~~~~~~~~~~ There are two configuration parameters related to BGP which are specified in ``bgp_dragent.ini``. * bgp_speaker_driver, to define BGP speaker driver class. Default is os-ken (neutron_dynamic_routing.services.bgp.agent.driver.os_ken.driver.OsKenBgpDriver). * bgp_router_id, to define BGP identity (typically an IPv4 address). Default is a unique loopback interface IP address. Common Driver API ----------------- Common Driver API is needed to provide a generic and consistent interface to different drivers. Each driver need to implement the provided `base driver class `_. BGP ~~~ Following interfaces need to be implemented by a driver for realizing BGP functionality. +--------------------------------+-----------------------------------------+ |API name |Description | +================================+=========================================+ |add_bgp_speaker() |Add a BGP Speaker | +--------------------------------+-----------------------------------------+ |delete_bgp_speaker() |Delete a BGP speaker | +--------------------------------+-----------------------------------------+ |add_bgp_peer() |Add a BGP peer | +--------------------------------+-----------------------------------------+ |delete_bgp_peer() |Delete a BGP peer | +--------------------------------+-----------------------------------------+ |advertise_route() |Add a new prefix to advertise | +--------------------------------+-----------------------------------------+ |withdraw_route() |Withdraw an advertised prefix | +--------------------------------+-----------------------------------------+ |get_bgp_speaker_statistics() |Collect BGP Speaker statistics | +--------------------------------+-----------------------------------------+ |get_bgp_peer_statistics() |Collect BGP Peer statistics | +--------------------------------+-----------------------------------------+ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/contributor/index.rst0000664000175000017500000000306700000000000024703 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) =============== Developer Guide =============== In the Developer Guide, you will find information on neutron-dynamic-routing lower level programming APIs. There are sections that cover the core pieces of neutron-dynamic-routing, including its API, command-lines, database, system-design, alembic-migration etc. There are also subsections that describe specific drivers inside neutron-dynamic-routing. Finally, the developer guide includes information about testing and supported functionalities as well. This documentation is generated by the Sphinx toolkit and lives in the source tree. .. toctree:: :maxdepth: 2 contributing testing dragent-drivers ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/contributor/testing.rst0000664000175000017500000007364700000000000025264 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) Testing ======= Dynamic routing enables advertisement of self-service network prefixes to physical network devices that support a dynamic routing protocol, such as routers. The Neutron dynamic routing project consists of a service plugin-in and an agent that can advertise Neutron private network to outside of OpenStack. This document will describe how to test the Dynamic Routing functionalities, introduce what the environment architecture is for dynamic routing test and show how to setup dynamic routing environment using Devstack. Environment Architecture ------------------------- Use the following example architecture as a test environment to deploy neutron-dynamic-routing in your environment. The example architecture will deploy an all-in-one OpenStack and connect to an Ubuntu VM running Quagga as a router outside of OpenStack. See following:: +--------------+ 10.156.18.20 | | +----------------------------------| Quagga | | BGP Peering Session | Router | | | 172.24.4.3 | | +--------------+ | | |10.156.18.21 | External Network(172.24.4.0/24) -------------------------------------------------------------------------------------------------- |eth0 +---------------------------------------+ | | | | | br-ex | | +----------------+ | | | |172.24.4.1 | | +------------+ +-------+ | | | | |Router | | | | Dr-Agent | | | | | | | +-------+ | | +------------+ | | | ---------------- | | Tenant Network | | (10.0.0.0/24) | | | +---------------------------------------+ All-In-One OpenStack Installation Devstack Setup -------------- 1. Download devstack:: git clone https://opendev.org/openstack/devstack.git 2. Enable neutron-dynamic-routing by including this in your local.conf file:: [[local|localrc]] enable_plugin neutron-dynamic-routing https://opendev.org/openstack/neutron-dynamic-routing 3. Run devstack:: ./stack.sh Quagga Configure ---------------- Quagga is a network routing software available in most GNU/Linux, Solaris, FreeBSD, and NetBSD. It provides the implementation of OSPF, RIP, BGP and IS-IS. This section shows you how to install Quagga and then configure it on Ubuntu Linux. 1. Install Quagga using apt-get:: $ sudo apt-get install quagga quagga-doc 2. Create an empty file (/etc/quagga/zebra.conf) and set permissions. The Quagga files and configurations will be stored in /etc/quagga:: $ sudo touch /etc/quagga/zebra.conf $ sudo chown quagga.quagga /etc/quagga/zebra.conf $ sudo chmod 640 /etc/quagga/zebra.conf 3. Update quagga daemon file. You can enable/disable the daemons routing in the /etc/quagga/daemons file. Update /etc/quagga/daemons to enable zebra and bgp:: zebra=yes bgpd=yes ospfd=no ospf6d=no ripd=no ripngd=no isisd=no 4. Update /etc/quagga/zebra.conf:: # Zebra configuration # name of the router hostname quagga_1 password zebra # log log file /var/log/quagga/zebra.log 5. Update /etc/quagga/bgpd.conf:: # declare a router with local-as 1000 router bgp 1000 # set router-id to the network address we announce bgp router-id 10.156.18.20 # expose neighbor network which dynamic routing agent is using neighbor 10.156.18.21 remote-as 12345 # treat neutron dynamic routing agent as a passive peer in case # quagga keeps making futile connection attempts neighbor 10.156.18.21 passive # log log file /var/log/quagga/bgpd.log debug bgp events debug bgp filters debug bgp fsm debug bgp keepalives debug bgp updates 6. Restart the Quagga daemon:: $ sudo systemcl restart bgpd Service Test ------------- 1. As the dynamic routing is only supported by admin, source the devstack admin credentials:: $ . devstack/openrc admin admin 2. Verify that the neutron dynamic routing agent is running. .. code-block:: console $ openstack network agent list --agent-type bgp +--------------------+--------------------+--------------------+-------------------+-------+-------+---------------------+ | ID | Agent Type | Host | Availability Zone | Alive | State | Binary | +--------------------+--------------------+--------------------+-------------------+-------+-------+---------------------+ | 69ad386f-e055-4284 | BGP dynamic | devstack-bgp-dr | | :-) | UP | neutron-bgp-dragent | | -8c8e-ef9bd540705c | routing agent | | | | | | +--------------------+--------------------+--------------------+-------------------+-------+-------+---------------------+ 3. Create an address scope. The provider(external) and tenant networks must belong to the same address scope for the agent to advertise those tenant network prefixes. .. code-block:: console $ openstack address scope create --ip-version 4 --share public +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | id | c02c358a-9d35-43ea-8313-986b3e4a91c0 | | ip_version | 4 | | name | public | | project_id | b3ac05ef10bf441fbf4aa17f16ae1e6d | | shared | True | +------------+--------------------------------------+ 4. Create subnet pools. The provider and tenant networks use different pools. * Create the provider network pool. .. code-block:: console $ openstack subnet pool create --pool-prefix 172.24.4.0/24 \ --address-scope public provider +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | 18f74828-5f38-4d84-b030-ed642f2157c5 | | created_at | 2020-08-28T15:12:11Z | | default_prefixlen | 8 | | default_quota | None | | description | | | id | d812a10e-5981-4686-90c4-d6fff454b38a | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | provider | | prefixes | 172.24.4.0/24 | | project_id | 17c884da94bc4259b20ace3da6897297 | | revision_number | 0 | | shared | False | | tags | | | updated_at | 2020-08-28T15:12:11Z | +-------------------+--------------------------------------+ * Create tenant network pool. .. code-block:: console $ openstack subnet pool create --pool-prefix 10.0.0.0/16 \ --address-scope public --share selfservice +-------------------+--------------------------------------+ | Field | Value | +-------------------+--------------------------------------+ | address_scope_id | 18f74828-5f38-4d84-b030-ed642f2157c5 | | created_at | 2020-08-28T15:15:31Z | | default_prefixlen | 8 | | default_quota | None | | description | | | id | 8b9d1c9b-6aba-416f-8d10-1e7a0f6052f6 | | ip_version | 4 | | is_default | False | | max_prefixlen | 32 | | min_prefixlen | 8 | | name | selfservice | | prefixes | 10.0.0.0/16 | | project_id | 17c884da94bc4259b20ace3da6897297 | | revision_number | 0 | | shared | True | | tags | | | updated_at | 2020-08-28T15:15:31Z | +-------------------+--------------------------------------+ 5. Create the provider and tenant networks. * Create the provider network. .. code-block:: console $ openstack network create --external --provider-network-type flat \ --provider-physical-network public provider +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2020-08-28T15:24:07Z | | description | | | dns_domain | | | id | 18f9a9c0-f8f5-4360-822a-b687c1008bf7 | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | False | | is_vlan_transparent | None | | mtu | 1500 | | name | provider | | port_security_enabled | True | | project_id | 17c884da94bc4259b20ace3da6897297 | | provider:network_type | flat | | provider:physical_network | public | | provider:segmentation_id | None | | qos_policy_id | None | | revision_number | 1 | | router:external | External | | segments | None | | shared | False | | status | ACTIVE | | subnets | | | tags | | | updated_at | 2020-08-28T15:24:07Z | +---------------------------+--------------------------------------+ * Create a subnet on the provider network using an IP address allocation from the provider subnet pool. .. code-block:: console $ openstack subnet create --network provider --subnet-pool provider \ --prefix-length 24 provider +----------------------+--------------------------------------+ | Field | Value | +----------------------+--------------------------------------+ | allocation_pools | 172.24.4.2-172.24.4.254 | | cidr | 172.24.4.0/24 | | created_at | 2020-08-28T15:27:00Z | | description | | | dns_nameservers | | | dns_publish_fixed_ip | False | | enable_dhcp | True | | gateway_ip | 172.24.4.1 | | host_routes | | | id | 4ed8ac88-2c19-4f94-9362-7b301e743438 | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | provider | | network_id | 18f9a9c0-f8f5-4360-822a-b687c1008bf7 | | prefix_length | 24 | | project_id | 17c884da94bc4259b20ace3da6897297 | | revision_number | 0 | | segment_id | None | | service_types | | | subnetpool_id | a8fecc3d-a489-46ca-87fb-dff4e3371503 | | tags | | | updated_at | 2020-08-28T15:27:00Z | +----------------------+--------------------------------------+ * Create the tenant network. .. code-block:: console $ openstack network create private +---------------------------+--------------------------------------+ | Field | Value | +---------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2020-08-28T15:28:06Z | | description | | | dns_domain | | | id | 43643543-6edb-4c2b-a087-4553b75b6799 | | ipv4_address_scope | None | | ipv6_address_scope | None | | is_default | False | | is_vlan_transparent | None | | mtu | 1442 | | name | private | | port_security_enabled | True | | project_id | 17c884da94bc4259b20ace3da6897297 | | provider:network_type | geneve | | provider:physical_network | None | | provider:segmentation_id | 1 | | qos_policy_id | None | | revision_number | 1 | | router:external | Internal | | segments | None | | shared | False | | status | ACTIVE | | subnets | | | tags | | | updated_at | 2020-08-28T15:28:06Z | +---------------------------+--------------------------------------+ * Create a subnet on the tenant network using an IP address allocation from the private subnet pool. .. code-block:: console $ openstack subnet create --network private --subnet-pool selfservice \ --prefix-length 24 selfservice +----------------------+--------------------------------------+ | Field | Value | +----------------------+--------------------------------------+ | allocation_pools | 10.0.0.2-10.0.0.254 | | cidr | 10.0.0.0/24 | | created_at | 2020-08-28T15:29:20Z | | description | | | dns_nameservers | | | dns_publish_fixed_ip | False | | enable_dhcp | True | | gateway_ip | 10.0.0.1 | | host_routes | | | id | 12eec8cb-8303-4829-8b16-e9a75072fcb0 | | ip_version | 4 | | ipv6_address_mode | None | | ipv6_ra_mode | None | | name | selfservice | | network_id | 43643543-6edb-4c2b-a087-4553b75b6799 | | prefix_length | 24 | | project_id | 17c884da94bc4259b20ace3da6897297 | | revision_number | 0 | | segment_id | None | | service_types | | | subnetpool_id | 574f9d33-65b6-49a1-ab43-866085d06804 | | tags | | | updated_at | 2020-08-28T15:29:20Z | +----------------------+--------------------------------------+ 6. Create and configure router * Create a router. .. code-block:: console $ openstack router create router +-------------------------+--------------------------------------+ | Field | Value | +-------------------------+--------------------------------------+ | admin_state_up | UP | | availability_zone_hints | | | availability_zones | | | created_at | 2020-08-28T15:30:09Z | | description | | | external_gateway_info | null | | flavor_id | None | | id | 250e5cc1-4cfc-4dff-a3a3-eb206c071621 | | name | router | | project_id | 17c884da94bc4259b20ace3da6897297 | | revision_number | 1 | | routes | | | status | ACTIVE | | tags | | | updated_at | 2020-08-28T15:30:09Z | +-------------------------+--------------------------------------+ * Add the private subnet as an interface on the router. .. code-block:: console $ openstack router add subnet router selfservice * Add the provide network as a gateway on the router .. code-block:: console $ openstack router set --external-gateway provider router * Verify router ports. Note: from this result, you can see what the advertised routes are. .. code-block:: console $ openstack port list --router router +--------------------------------------+------+-------------------+----------------------------------------------------------------------------+--------+ | ID | Name | MAC Address | Fixed IP Addresses | Status | +--------------------------------------+------+-------------------+----------------------------------------------------------------------------+--------+ | 218c455f-f565-4e37-a2ac-999da24efa66 | | fa:16:3e:74:d8:61 | ip_address='10.0.0.1', subnet_id='12eec8cb-8303-4829-8b16-e9a75072fcb0' | ACTIVE | | 44dcb7d3-b444-4177-82a1-233b1f3bed23 | | fa:16:3e:5b:4b:2d | ip_address='172.24.4.24', subnet_id='4ed8ac88-2c19-4f94-9362-7b301e743438' | ACTIVE | +--------------------------------------+------+-------------------+----------------------------------------------------------------------------+--------+ 7. Create and configure the BGP speaker The BGP speaker advertised the next-hop IP address for the tenant network prefix. * Create the BGP speaker. Replace LOCAL_AS with an appropriate local autonomous system number. The example configuration uses AS 12345. .. code-block:: console $ openstack bgp speaker create --ip-version 4 \ --local-as LOCAL_AS bgp-speaker +-----------------------------------+--------------------------------------+ | Field | Value | +-----------------------------------+--------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 19cdf669-4d4d-442f-bbf6-510a97ad8cd8 | | ip_version | 4 | | local_as | 12345 | | name | bgp-speaker | | networks | [] | | peers | [] | | project_id | 17c884da94bc4259b20ace3da6897297 | +-----------------------------------+--------------------------------------+ * Associate the BGP speaker with the provider network. A BGP speaker requires association with a provider network to determine eligible prefixes. After the association, the BGP speaker can advertise the tenant network prefixes with the corresponding router as the next-hop IP address. .. code-block:: console $ openstack bgp speaker add network bgp-speaker provider * Verify the association of the provider network with the BGP speaker. Checking the ``networks`` attribute. .. code-block:: console $ openstack bgp speaker show bgp-speaker +-----------------------------------+------------------------------------------+ | Field | Value | +-----------------------------------+------------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 19cdf669-4d4d-442f-bbf6-510a97ad8cd8 | | ip_version | 4 | | local_as | 12345 | | name | bgp-speaker | | networks | ['18f9a9c0-f8f5-4360-822a-b687c1008bf7'] | | peers | [] | | project_id | 17c884da94bc4259b20ace3da6897297 | +-----------------------------------+------------------------------------------+ * Verify the prefixes and next-hop ip addresses that the BGP speaker advertises. .. code-block:: console $ openstack bgp speaker list advertised routes bgp-speaker +-------------+------------+ | destination | next_hop | +-------------+------------+ | 10.0.0.0/24 | 172.24.4.3 | +-------------+------------+ * Create a BGP peer. Here the BGP peer is pointed to the quagga VM. Replace REMOTE_AS with an appropriate remote autonomous system number. The example configuration uses AS 12345 which triggers iBGP peering. .. code-block:: console $ openstack bgp peer create --peer-ip 10.156.18.20 \ --remote-as REMOTE_AS bgp-peer +------------+--------------------------------------+ | Field | Value | +------------+--------------------------------------+ | auth_type | none | | id | 37291604-de77-4333-8f27-4ca336e021f2 | | name | bgp-peer | | peer_ip | 10.156.18.20 | | project_id | 17c884da94bc4259b20ace3da6897297 | | remote_as | 12345 | +------------+--------------------------------------+ * Add a BGP peer to the BGP speaker. .. code-block:: console $ openstack bgp speaker add peer bgp-speaker bgp-peer * Verify the association of the BGP peer with the BGP speaker. Checking the ``peers`` attribute. .. code-block:: console $ openstack bgp speaker show bgp-speaker +-----------------------------------+------------------------------------------+ | Field | Value | +-----------------------------------+------------------------------------------+ | advertise_floating_ip_host_routes | True | | advertise_tenant_networks | True | | id | 19cdf669-4d4d-442f-bbf6-510a97ad8cd8 | | ip_version | 4 | | local_as | 12345 | | name | bgp-speaker | | networks | ['18f9a9c0-f8f5-4360-822a-b687c1008bf7'] | | peers | ['37291604-de77-4333-8f27-4ca336e021f2'] | | project_id | 17c884da94bc4259b20ace3da6897297 | +-----------------------------------+------------------------------------------+ 8. Schedule the BGP speaker to an agent. * Schedule the BGP speaker to ``BGP dynamic routing agent`` With the default scheduler configuration, the first BGP speaker is scheduled to the first dynamic routing agent automatically. So for a simple setup, there is nothing to be done here. * Verify scheduling of the BGP speaker to the agent. .. code-block:: console $ openstack bgp dragent list --bgp-speaker bgp-speaker +--------------------------------------+---------------------------+----------------+-------+ | id | host | admin_state_up | alive | +--------------------------------------+---------------------------+----------------+-------+ | 239996c8-2d59-4131-98b8-d64372c812cc | devstack-bgp-dr | True | :-) | +--------------------------------------+---------------------------+----------------+-------+ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/index.rst0000664000175000017500000000306100000000000022323 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) =================================================== Welcome to neutron-dynamic-routing's documentation! =================================================== .. NOTE(amotoki): toctree_only=False is specified in latex_documents in doc/source/conf.py to get a better structure of the PDF doc. This means the content of this file (index.rst) is NOT rendered in the generated PDF file. .. include:: ../../README.rst Contents ======== .. toctree:: :maxdepth: 2 install/index admin/index configuration/index reference/index cli/index contributor/index Indices and tables ================== * :ref:`genindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/doc/source/install/0000775000175000017500000000000000000000000022130 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/install/index.rst0000664000175000017500000000221400000000000023770 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) ============ Installation ============ At the command line:: $ pip install neutron-dynamic-routing Or, if you have virtualenv wrapper installed:: $ mkvirtualenv neutron-dynamic-routing $ pip install neutron-dynamic-routing ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/doc/source/reference/0000775000175000017500000000000000000000000022420 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/doc/source/reference/index.rst0000664000175000017500000000205600000000000024264 0ustar00zuulzuul00000000000000.. Copyright 2016 Huawei Technologies India Pvt Limited. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Convention for heading levels in Neutron devref: ======= Heading 0 (reserved for the title in a document) ------- Heading 1 ~~~~~~~ Heading 2 +++++++ Heading 3 ''''''' Heading 4 (Avoid deeper levels because they do not render well.) API === The reference of the OpenStack neutron-dynamic-routing API is found at https://docs.openstack.org/api-ref/network/#bgp-dynamic-routing. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/etc/0000775000175000017500000000000000000000000017170 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/etc/README.txt0000664000175000017500000000077100000000000020673 0ustar00zuulzuul00000000000000To generate the sample neutron-dynamic-routing configuration files and the sample policy file, run the following commands respectively from the top level of the neutron-dynamic-routing directory: tox -e genconfig tox -e genpolicy If a 'tox' environment is unavailable, then you can run the following commands instead to generate the configuration files and the policy file: ./tools/generate_config_file_samples.sh oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/etc/oslo-config-generator/0000775000175000017500000000000000000000000023373 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/etc/oslo-config-generator/bgp_dragent.ini0000664000175000017500000000013200000000000026344 0ustar00zuulzuul00000000000000[DEFAULT] output_file = etc/bgp_dragent.ini.sample wrap_width = 79 namespace = bgp.agent ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/etc/oslo-policy-generator/0000775000175000017500000000000000000000000023425 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/etc/oslo-policy-generator/policy.conf0000664000175000017500000000012300000000000025567 0ustar00zuulzuul00000000000000[DEFAULT] output_file = etc/policy.yaml.sample namespace = neutron-dynamic-routing ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/lower-constraints.txt0000664000175000017500000000510700000000000022656 0ustar00zuulzuul00000000000000alabaster==0.7.10 alembic==0.9.6 amqp==2.1.1 appdirs==1.3.0 asn1crypto==0.23.0 Babel==2.3.4 beautifulsoup4==4.6.0 cachetools==2.0.0 cffi==1.14.0 cliff==2.8.0 cmd2==0.8.0 contextlib2==0.4.0 coverage==4.0 cryptography==2.7 debtcollector==1.2.0 decorator==4.0.0 deprecation==1.0 docutils==0.12 dogpile.cache==0.8.0 dulwich==0.15.0 eventlet==0.18.2 extras==1.0.0 fasteners==0.7.0 fixtures==3.0.0 flake8==3.8.0 flake8-import-order==0.12 future==0.16.0 futurist==1.2.0 greenlet==0.4.15 hacking==3.2.0 httplib2==0.9.1 idna==2.6 imagesize==0.7.1 iso8601==0.1.11 Jinja2==2.10 jmespath==0.9.0 jsonpatch==1.16 jsonpointer==1.13 jsonschema==2.6.0 keystoneauth1==3.4.0 keystonemiddleware==4.17.0 kombu==4.0.0 linecache2==1.0.0 logutils==0.3.5 Mako==1.0.7 MarkupSafe==1.1.1 mccabe==0.6.1 monotonic==0.6 mox3==0.20.0 msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 neutron==14.0.0.0b1 neutron-lib==1.26.0 openstackdocstheme==2.2.1 openstacksdk==0.11.2 os-client-config==1.28.0 os-ken==0.3.0 os-service-types==1.2.0 os-xenapi==0.3.1 osc-lib==1.8.0 oslo.cache==1.26.0 oslo.concurrency==3.26.0 oslo.config==5.2.0 oslo.context==2.22.0 oslo.db==4.44.0 oslo.i18n==3.15.3 oslo.log==3.36.0 oslo.messaging==5.29.0 oslo.middleware==3.31.0 oslo.policy==2.0.0 oslo.privsep==1.23.0 oslo.reports==1.18.0 oslo.rootwrap==5.8.0 oslo.serialization==2.18.0 oslo.service==1.24.0 oslo.utils==4.5.0 oslo.versionedobjects==1.31.2 oslotest==3.2.0 osprofiler==1.4.0 ovs==2.8.0 ovsdbapp==0.10.0 paramiko==2.0.0 Paste==2.0.2 PasteDeploy==1.5.0 pbr==2.0.0 pecan==1.3.2 pep8==1.5.7 pika==0.10.0 pika-pool==0.1.3 positional==1.2.1 prettytable==0.7.2 psutil==3.2.2 pyasn1==0.1.8 pycadf==1.1.0 pycodestyle==2.6.0 pycparser==2.18 pyflakes==2.2.0 Pygments==2.2.0 pyinotify==0.9.6 pyparsing==2.1.0 pyperclip==1.5.27 pyroute2==0.5.13 python-dateutil==2.5.3 python-designateclient==2.7.0 python-editor==1.0.3 python-keystoneclient==3.8.0 python-mimeparse==1.6.0 python-neutronclient==6.7.0 python-novaclient==9.1.0 python-subunit==1.0.0 pytz==2013.6 PyYAML==3.13 reno==3.1.0 repoze.lru==0.7 requests==2.14.2 requestsexceptions==1.2.0 rfc3986==0.3.1 Routes==2.3.1 simplejson==3.5.1 snowballstemmer==1.2.1 Sphinx==2.0.0 sphinxcontrib-svg2pdfconverter==0.1.0 # BSD sphinxcontrib-websupport==1.0.1 SQLAlchemy==1.3.3 sqlalchemy-migrate==0.11.0 sqlparse==0.2.2 statsd==3.2.1 stestr==1.0.0 stevedore==1.20.0 tempest==17.1.0 Tempita==0.5.2 tenacity==3.2.1 testrepository==0.0.18 testresources==2.0.0 testscenarios==0.4 testtools==2.2.0 tinyrpc==0.6 traceback2==1.4.0 unittest2==1.1.0 urllib3==1.21.1 vine==1.1.4 waitress==1.1.0 WebOb==1.8.2 WebTest==2.0.27 wrapt==1.7.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/0000775000175000017500000000000000000000000023362 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/__init__.py0000664000175000017500000000125000000000000025471 0ustar00zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import gettext gettext.install('neutron') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/_i18n.py0000664000175000017500000000253300000000000024655 0ustar00zuulzuul00000000000000# All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import oslo_i18n DOMAIN = "neutron_dynamic_routing" _translators = oslo_i18n.TranslatorFactory(domain=DOMAIN) # The primary translation function using the well-known name "_" _ = _translators.primary # The contextual translation function using the name "_C" _C = _translators.contextual_form # The plural translation function using the name "_P" _P = _translators.plural_form # Translators for log levels. # # The abbreviated names are meant to reflect the usual use of a short # name like '_'. The "L" is for "log" and the other letter comes from # the level. _LI = _translators.log_info _LW = _translators.log_warning _LE = _translators.log_error _LC = _translators.log_critical def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/0000775000175000017500000000000000000000000024133 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/__init__.py0000664000175000017500000000000000000000000026232 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/0000775000175000017500000000000000000000000024717 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/__init__.py0000664000175000017500000000000000000000000027016 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/0000775000175000017500000000000000000000000027740 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/__init__.py0000664000175000017500000000000000000000000032037 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/bgp_dr_rpc_agent_api.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/agentnotifiers/bgp_dr_rpc_agent_api.p0000664000175000017500000001172200000000000034234 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import rpc as n_rpc import oslo_messaging from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts class BgpDrAgentNotifyApi(object): """API for plugin to notify BGP DrAgent. This class implements the client side of an rpc interface. The server side is neutron_dynamic_routing.services.bgp.agent.bgp_dragent.BgpDrAgent. For more information about rpc interfaces, please see https://docs.openstack.org/neutron/latest/contributor/internals/rpc_api.html. """ def __init__(self, topic=bgp_consts.BGP_DRAGENT): target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) self.topic = topic def agent_updated(self, context, admin_state_up, host): """Tell BgpDrAgent that agent was updated. This effectively tells the bgp_dragent to resync. """ self._notification_host_cast(context, 'agent_updated', {'admin_state_up': admin_state_up}, host) def bgp_routes_advertisement(self, context, bgp_speaker_id, routes, host): """Tell BgpDrAgent to begin advertising the given route. Invoked on FIP association, adding router port to a tenant network, and new DVR port-host bindings, and subnet creation(?). """ self._notification_host_cast(context, 'bgp_routes_advertisement_end', {'advertise_routes': {'speaker_id': bgp_speaker_id, 'routes': routes}}, host) def bgp_routes_withdrawal(self, context, bgp_speaker_id, routes, host): """Tell BgpDrAgent to stop advertising the given route. Invoked on FIP disassociation, removal of a router port on a network, and removal of DVR port-host binding, and subnet delete(?). """ self._notification_host_cast(context, 'bgp_routes_withdrawal_end', {'withdraw_routes': {'speaker_id': bgp_speaker_id, 'routes': routes}}, host) def bgp_peer_disassociated(self, context, bgp_speaker_id, bgp_peer_ip, host): """Tell BgpDrAgent about a BGP Peer disassociation. This effectively tells the BgpDrAgent to stop a peering session. """ self._notification_host_cast(context, 'bgp_peer_disassociation_end', {'bgp_peer': {'speaker_id': bgp_speaker_id, 'peer_ip': bgp_peer_ip}}, host) def bgp_peer_associated(self, context, bgp_speaker_id, bgp_peer_id, host): """Tell BgpDrAgent about a new BGP Peer association. This effectively tells the bgp_dragent to open a peering session. """ self._notification_host_cast(context, 'bgp_peer_association_end', {'bgp_peer': {'speaker_id': bgp_speaker_id, 'peer_id': bgp_peer_id}}, host) def bgp_speaker_created(self, context, bgp_speaker_id, host): """Tell BgpDrAgent about the creation of a BGP Speaker. Because a BGP Speaker can be created with BgpPeer binding in place, we need to inform the BgpDrAgent of a new BGP Speaker in case a peering session needs to opened immediately. """ self._notification_host_cast(context, 'bgp_speaker_create_end', {'bgp_speaker': {'id': bgp_speaker_id}}, host) def bgp_speaker_removed(self, context, bgp_speaker_id, host): """Tell BgpDrAgent about the removal of a BGP Speaker. Because a BGP Speaker can be removed with BGP Peer binding in place, we need to inform the BgpDrAgent of the removal of a BGP Speaker in case peering sessions need to be stopped. """ self._notification_host_cast(context, 'bgp_speaker_remove_end', {'bgp_speaker': {'id': bgp_speaker_id}}, host) def _notification_host_cast(self, context, method, payload, host): """Send payload to BgpDrAgent in the cast mode""" cctxt = self.client.prepare(topic=self.topic, server=host) cctxt.cast(context, method, payload=payload) def _notification_host_call(self, context, method, payload, host): """Send payload to BgpDrAgent in the call mode""" cctxt = self.client.prepare(topic=self.topic, server=host) cctxt.call(context, method, payload=payload) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/callbacks/0000775000175000017500000000000000000000000026636 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/callbacks/__init__.py0000664000175000017500000000000000000000000030735 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/callbacks/resources.py0000664000175000017500000000113100000000000031216 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. BGP_SPEAKER = 'bgp_speaker' ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/handlers/0000775000175000017500000000000000000000000026517 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/handlers/__init__.py0000664000175000017500000000000000000000000030616 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/api/rpc/handlers/bgp_speaker_rpc.py0000664000175000017500000000466500000000000032232 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib.api.definitions import bgp as bgp_ext from neutron_lib.plugins import directory import oslo_messaging class BgpSpeakerRpcCallback(object): """BgpDrAgent RPC callback in plugin implementations. This class implements the server side of an RPC interface. The client side of this interface can be found in neutron_dynamic_routing.services.bgp.agent.bgp_dragent.BgpDrPluginApi. For more information about changing RPC interfaces, see https://docs.openstack.org/neutron/latest/ contributor/internals/rpc_api.html. """ # API version history: # 1.0 BGPDRPluginApi BASE_RPC_API_VERSION target = oslo_messaging.Target(version='1.0') @property def plugin(self): if not hasattr(self, '_plugin'): self._plugin = directory.get_plugin(bgp_ext.ALIAS) return self._plugin def get_bgp_speaker_info(self, context, bgp_speaker_id): """Return BGP Speaker details such as peer list and local_as. Invoked by the BgpDrAgent to lookup the details of a BGP Speaker. """ return self.plugin.get_bgp_speaker_with_advertised_routes( context, bgp_speaker_id) def get_bgp_peer_info(self, context, bgp_peer_id): """Return BgpPeer details such as IP, remote_as, and credentials. Invoked by the BgpDrAgent to lookup the details of a BGP peer. """ return self.plugin.get_bgp_peer(context, bgp_peer_id, ['peer_ip', 'remote_as', 'auth_type', 'password']) def get_bgp_speakers(self, context, host=None, **kwargs): """Returns the list of all BgpSpeakers. Typically invoked by the BgpDrAgent as part of its bootstrap process. """ return self.plugin.get_bgp_speakers_for_agent_host(context, host) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/0000775000175000017500000000000000000000000024125 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/__init__.py0000664000175000017500000000000000000000000026224 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/eventlet/0000775000175000017500000000000000000000000025753 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/eventlet/__init__.py0000664000175000017500000000170400000000000030066 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import eventlet eventlet.monkey_patch() # Monkey patch the original current_thread to use the up-to-date _active # global variable. See https://bugs.launchpad.net/bugs/1863021 and # https://github.com/eventlet/eventlet/issues/592 import __original_module_threading as orig_threading # noqa import threading # noqa orig_threading.current_thread.__globals__['_active'] = threading._active ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/eventlet/agents/0000775000175000017500000000000000000000000027234 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/eventlet/agents/__init__.py0000664000175000017500000000000000000000000031333 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/cmd/eventlet/agents/bgp_dragent.py0000664000175000017500000000131200000000000032057 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_dynamic_routing.services.bgp.agent import entry as bgp_dragent def main(): bgp_dragent.main() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/0000775000175000017500000000000000000000000023747 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/__init__.py0000664000175000017500000000000000000000000026046 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/bgp_db.py0000664000175000017500000017100200000000000025537 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import netaddr from neutron.db import l3_dvr_db from neutron.db.models import address_scope as address_scope_db from neutron.db.models import l3 as l3_db from neutron.db.models import l3_attrs as l3_attrs_db from neutron.db import models_v2 from neutron.objects import ports from neutron.objects import subnet as subnet_obj from neutron.objects import subnetpool as subnetpool_obj from neutron.plugins.ml2 import models as ml2_models from neutron_lib.api.definitions import bgp as bgp_ext from neutron_lib.api import validators from neutron_lib import constants as lib_consts from neutron_lib.db import api as db_api from neutron_lib.db import model_base from neutron_lib.db import model_query from neutron_lib.db import utils as db_utils from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import l3 as l3_exc from oslo_db import exception as oslo_db_exc from oslo_utils import uuidutils import sqlalchemy as sa from sqlalchemy import and_ from sqlalchemy import orm from sqlalchemy.orm import aliased from sqlalchemy.orm import exc as sa_exc from neutron_dynamic_routing._i18n import _ from neutron_dynamic_routing.extensions import bgp DEVICE_OWNER_ROUTER_GW = lib_consts.DEVICE_OWNER_ROUTER_GW DEVICE_OWNER_ROUTER_INTF = lib_consts.DEVICE_OWNER_ROUTER_INTF DEVICE_OWNER_DVR_INTERFACE = lib_consts.DEVICE_OWNER_DVR_INTERFACE class BgpSpeakerPeerBinding(model_base.BASEV2): """Represents a mapping between BGP speaker and BGP peer""" __tablename__ = 'bgp_speaker_peer_bindings' bgp_speaker_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgp_speakers.id', ondelete='CASCADE'), nullable=False, primary_key=True) bgp_peer_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgp_peers.id', ondelete='CASCADE'), nullable=False, primary_key=True) class BgpSpeakerNetworkBinding(model_base.BASEV2): """Represents a mapping between a network and BGP speaker""" __tablename__ = 'bgp_speaker_network_bindings' bgp_speaker_id = sa.Column(sa.String(length=36), sa.ForeignKey('bgp_speakers.id', ondelete='CASCADE'), nullable=False, primary_key=True) network_id = sa.Column(sa.String(length=36), sa.ForeignKey('networks.id', ondelete='CASCADE'), nullable=False, primary_key=True) ip_version = sa.Column(sa.Integer, nullable=False, autoincrement=False, primary_key=True) class BgpSpeaker(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a BGP speaker""" __tablename__ = 'bgp_speakers' name = sa.Column(sa.String(255), nullable=False) local_as = sa.Column(sa.BigInteger(), nullable=False, autoincrement=False) advertise_floating_ip_host_routes = sa.Column(sa.Boolean, nullable=False) advertise_tenant_networks = sa.Column(sa.Boolean, nullable=False) peers = orm.relationship(BgpSpeakerPeerBinding, backref='bgp_speaker_peer_bindings', cascade='all, delete, delete-orphan', lazy='joined') networks = orm.relationship(BgpSpeakerNetworkBinding, backref='bgp_speaker_network_bindings', cascade='all, delete, delete-orphan', lazy='joined') ip_version = sa.Column(sa.Integer, nullable=False, autoincrement=False) class BgpPeer(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a BGP routing peer.""" __tablename__ = 'bgp_peers' name = sa.Column(sa.String(255), nullable=False) peer_ip = sa.Column(sa.String(64), nullable=False) remote_as = sa.Column(sa.BigInteger(), nullable=False, autoincrement=False) auth_type = sa.Column(sa.String(16), nullable=False) password = sa.Column(sa.String(255), nullable=True) class BgpDbMixin(object): def create_bgp_speaker(self, context, bgp_speaker): uuid = uuidutils.generate_uuid() self._save_bgp_speaker(context, bgp_speaker, uuid) return self.get_bgp_speaker(context, uuid) def get_bgp_speakers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): with db_api.CONTEXT_READER.using(context): return model_query.get_collection( context, BgpSpeaker, self._make_bgp_speaker_dict, filters=filters, fields=fields, sorts=sorts, limit=limit, page_reverse=page_reverse) def get_bgp_speaker(self, context, bgp_speaker_id, fields=None): with db_api.CONTEXT_READER.using(context): bgp_speaker = self._get_bgp_speaker(context, bgp_speaker_id) return self._make_bgp_speaker_dict(bgp_speaker, fields) def get_bgp_speaker_with_advertised_routes(self, context, bgp_speaker_id): bgp_speaker_attrs = ['id', 'local_as', 'tenant_id'] bgp_peer_attrs = ['peer_ip', 'remote_as', 'auth_type', 'password'] with db_api.CONTEXT_READER.using(context): bgp_speaker = self.get_bgp_speaker(context, bgp_speaker_id, fields=bgp_speaker_attrs) res = dict((k, bgp_speaker[k]) for k in bgp_speaker_attrs) res['peers'] = self.get_bgp_peers_by_bgp_speaker(context, bgp_speaker['id'], fields=bgp_peer_attrs) res['advertised_routes'] = self.get_routes_by_bgp_speaker_id( context, bgp_speaker_id) return res def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker): bp = bgp_speaker[bgp_ext.BGP_SPEAKER_BODY_KEY_NAME] with db_api.CONTEXT_WRITER.using(context): bgp_speaker_db = self._get_bgp_speaker(context, bgp_speaker_id) bgp_speaker_db.update(bp) bgp_speaker_dict = self._make_bgp_speaker_dict(bgp_speaker_db) return bgp_speaker_dict def _save_bgp_speaker(self, context, bgp_speaker, uuid): ri = bgp_speaker[bgp_ext.BGP_SPEAKER_BODY_KEY_NAME] ri['tenant_id'] = context.tenant_id with db_api.CONTEXT_WRITER.using(context): res_keys = ['local_as', 'tenant_id', 'name', 'ip_version', 'advertise_floating_ip_host_routes', 'advertise_tenant_networks'] res = dict((k, ri[k]) for k in res_keys) res['id'] = uuid bgp_speaker_db = BgpSpeaker(**res) context.session.add(bgp_speaker_db) def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): bgp_peer_id = self._get_id_for(bgp_peer_info, 'bgp_peer_id') self._save_bgp_speaker_peer_binding(context, bgp_speaker_id, bgp_peer_id) return {'bgp_peer_id': bgp_peer_id} def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): bgp_peer_id = self._get_id_for(bgp_peer_info, 'bgp_peer_id') self._remove_bgp_speaker_peer_binding(context, bgp_speaker_id, bgp_peer_id) return {'bgp_peer_id': bgp_peer_id} def add_gateway_network(self, context, bgp_speaker_id, network_info): network_id = self._get_id_for(network_info, 'network_id') with db_api.CONTEXT_WRITER.using(context): try: self._save_bgp_speaker_network_binding(context, bgp_speaker_id, network_id) except oslo_db_exc.DBDuplicateEntry: raise bgp.BgpSpeakerNetworkBindingError( network_id=network_id, bgp_speaker_id=bgp_speaker_id) return {'network_id': network_id} def remove_gateway_network(self, context, bgp_speaker_id, network_info): with db_api.CONTEXT_WRITER.using(context): network_id = self._get_id_for(network_info, 'network_id') self._remove_bgp_speaker_network_binding(context, bgp_speaker_id, network_id) return {'network_id': network_id} def delete_bgp_speaker(self, context, bgp_speaker_id): with db_api.CONTEXT_WRITER.using(context): bgp_speaker_db = self._get_bgp_speaker(context, bgp_speaker_id) context.session.delete(bgp_speaker_db) def create_bgp_peer(self, context, bgp_peer): ri = bgp_peer[bgp_ext.BGP_PEER_BODY_KEY_NAME] auth_type = ri.get('auth_type') password = ri.get('password') if auth_type == 'md5' and not password: raise bgp.InvalidBgpPeerMd5Authentication() with db_api.CONTEXT_WRITER.using(context): res_keys = ['tenant_id', 'name', 'remote_as', 'peer_ip', 'auth_type', 'password'] res = dict((k, ri[k]) for k in res_keys) res['id'] = uuidutils.generate_uuid() bgp_peer_db = BgpPeer(**res) context.session.add(bgp_peer_db) peer = self._make_bgp_peer_dict(bgp_peer_db) peer.pop('password') return peer def get_bgp_peers(self, context, fields=None, filters=None, sorts=None, limit=None, marker=None, page_reverse=False): return model_query.get_collection(context, BgpPeer, self._make_bgp_peer_dict, filters=filters, fields=fields, sorts=sorts, limit=limit, page_reverse=page_reverse) def get_bgp_peers_by_bgp_speaker(self, context, bgp_speaker_id, fields=None): filters = [BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerPeerBinding.bgp_peer_id == BgpPeer.id] with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpPeer) query = query.filter(*filters) return [self._make_bgp_peer_dict(x, fields) for x in query.all()] def get_bgp_peer(self, context, bgp_peer_id, fields=None): bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id) return self._make_bgp_peer_dict(bgp_peer_db, fields=fields) def delete_bgp_peer(self, context, bgp_peer_id): with db_api.CONTEXT_WRITER.using(context): bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id) context.session.delete(bgp_peer_db) def update_bgp_peer(self, context, bgp_peer_id, bgp_peer): bp = bgp_peer[bgp_ext.BGP_PEER_BODY_KEY_NAME] with db_api.CONTEXT_WRITER.using(context): bgp_peer_db = self._get_bgp_peer(context, bgp_peer_id) if ((bp.get('password') is not None) and (bgp_peer_db['auth_type'] == 'none')): raise bgp.BgpPeerNotAuthenticated(bgp_peer_id=bgp_peer_id) bgp_peer_db.update(bp) bgp_peer_dict = self._make_bgp_peer_dict(bgp_peer_db) return bgp_peer_dict def _get_bgp_speaker(self, context, bgp_speaker_id): try: return model_query.get_by_id(context, BgpSpeaker, bgp_speaker_id) except sa_exc.NoResultFound: raise bgp.BgpSpeakerNotFound(id=bgp_speaker_id) def _get_bgp_speaker_ids_by_router(self, context, router_id): with db_api.CONTEXT_READER.using(context): network_binding = aliased(BgpSpeakerNetworkBinding) r_port = aliased(l3_db.RouterPort) query = context.session.query(network_binding.bgp_speaker_id) query = query.filter( r_port.router_id == router_id, r_port.port_type == lib_consts.DEVICE_OWNER_ROUTER_GW, r_port.port_id == models_v2.Port.id, models_v2.Port.network_id == network_binding.network_id) return [binding.bgp_speaker_id for binding in query.all()] def _get_bgp_speaker_ids_by_binding_network(self, context, network_id): with db_api.CONTEXT_READER.using(context): query = context.session.query( BgpSpeakerNetworkBinding.bgp_speaker_id) query = query.filter( BgpSpeakerNetworkBinding.network_id == network_id) return query.all() def get_advertised_routes(self, context, bgp_speaker_id): routes = self.get_routes_by_bgp_speaker_id(context, bgp_speaker_id) return self._make_advertised_routes_dict(routes) def _get_id_for(self, resource, id_name): try: uuid = resource[id_name] msg = validators.validate_uuid(uuid) except KeyError: msg = _("%s must be specified") % id_name if msg: raise n_exc.BadRequest(resource=bgp_ext.BGP_SPEAKER_RESOURCE_NAME, msg=msg) return uuid def _get_bgp_peers_by_bgp_speaker_binding(self, context, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpPeer) query = query.filter( BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerPeerBinding.bgp_peer_id == BgpPeer.id) return query.all() def _save_bgp_speaker_peer_binding(self, context, bgp_speaker_id, bgp_peer_id): with db_api.CONTEXT_WRITER.using(context): try: bgp_speaker = model_query.get_by_id(context, BgpSpeaker, bgp_speaker_id) except sa_exc.NoResultFound: raise bgp.BgpSpeakerNotFound(id=bgp_speaker_id) try: bgp_peer = model_query.get_by_id(context, BgpPeer, bgp_peer_id) except sa_exc.NoResultFound: raise bgp.BgpPeerNotFound(id=bgp_peer_id) peers = self._get_bgp_peers_by_bgp_speaker_binding(context, bgp_speaker_id) self._validate_peer_ips(bgp_speaker_id, peers, bgp_peer) binding = BgpSpeakerPeerBinding(bgp_speaker_id=bgp_speaker.id, bgp_peer_id=bgp_peer.id) context.session.add(binding) def _validate_peer_ips(self, bgp_speaker_id, current_peers, new_peer): for peer in current_peers: if peer.peer_ip == new_peer.peer_ip: raise bgp.DuplicateBgpPeerIpException( bgp_peer_id=new_peer.id, peer_ip=new_peer.peer_ip, bgp_speaker_id=bgp_speaker_id) def _remove_bgp_speaker_peer_binding(self, context, bgp_speaker_id, bgp_peer_id): with db_api.CONTEXT_WRITER.using(context): try: binding = self._get_bgp_speaker_peer_binding(context, bgp_speaker_id, bgp_peer_id) except sa_exc.NoResultFound: raise bgp.BgpSpeakerPeerNotAssociated( bgp_peer_id=bgp_peer_id, bgp_speaker_id=bgp_speaker_id) context.session.delete(binding) def _save_bgp_speaker_network_binding(self, context, bgp_speaker_id, network_id): with db_api.CONTEXT_WRITER.using(context): try: bgp_speaker = model_query.get_by_id(context, BgpSpeaker, bgp_speaker_id) except sa_exc.NoResultFound: raise bgp.BgpSpeakerNotFound(id=bgp_speaker_id) try: network = model_query.get_by_id(context, models_v2.Network, network_id) except sa_exc.NoResultFound: raise n_exc.NetworkNotFound(net_id=network_id) binding = BgpSpeakerNetworkBinding( bgp_speaker_id=bgp_speaker.id, network_id=network.id, ip_version=bgp_speaker.ip_version) context.session.add(binding) def _remove_bgp_speaker_network_binding(self, context, bgp_speaker_id, network_id): with db_api.CONTEXT_WRITER.using(context): try: binding = self._get_bgp_speaker_network_binding( context, bgp_speaker_id, network_id) except sa_exc.NoResultFound: raise bgp.BgpSpeakerNetworkNotAssociated( network_id=network_id, bgp_speaker_id=bgp_speaker_id) context.session.delete(binding) def _make_bgp_speaker_dict(self, bgp_speaker, fields=None): attrs = {'id', 'local_as', 'tenant_id', 'name', 'ip_version', 'advertise_floating_ip_host_routes', 'advertise_tenant_networks'} peer_bindings = bgp_speaker['peers'] network_bindings = bgp_speaker['networks'] res = dict((k, bgp_speaker[k]) for k in attrs) res['peers'] = [x.bgp_peer_id for x in peer_bindings] res['networks'] = [x.network_id for x in network_bindings] return db_utils.resource_fields(res, fields) def _make_advertised_routes_dict(self, routes): return {'advertised_routes': list(routes)} def _get_bgp_peer(self, context, bgp_peer_id): try: return model_query.get_by_id(context, BgpPeer, bgp_peer_id) except sa_exc.NoResultFound: raise bgp.BgpPeerNotFound(id=bgp_peer_id) def _get_bgp_speaker_peer_binding(self, context, bgp_speaker_id, bgp_peer_id): query = model_query.query_with_hooks(context, BgpSpeakerPeerBinding) return query.filter( BgpSpeakerPeerBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerPeerBinding.bgp_peer_id == bgp_peer_id).one() def _get_bgp_speaker_network_binding(self, context, bgp_speaker_id, network_id): query = model_query.query_with_hooks(context, BgpSpeakerNetworkBinding) return query.filter( BgpSpeakerNetworkBinding.bgp_speaker_id == bgp_speaker_id, BgpSpeakerNetworkBinding.network_id == network_id).one() def _make_bgp_peer_dict(self, bgp_peer, fields=None): attrs = ['tenant_id', 'id', 'name', 'peer_ip', 'remote_as', 'auth_type', 'password'] res = dict((k, bgp_peer[k]) for k in attrs) return db_utils.resource_fields(res, fields) def _get_address_scope_ids_for_bgp_speaker(self, context, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): binding = aliased(BgpSpeakerNetworkBinding) address_scope = aliased(address_scope_db.AddressScope) query = context.session.query(address_scope) query = query.filter( binding.bgp_speaker_id == bgp_speaker_id, models_v2.Subnet.ip_version == binding.ip_version, models_v2.Subnet.network_id == binding.network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id) return [scope.id for scope in query.all()] def get_routes_by_bgp_speaker_id(self, context, bgp_speaker_id): """Get all routes that should be advertised by a BgpSpeaker.""" with db_api.CONTEXT_READER.using(context): net_routes = self._get_tenant_network_routes_by_bgp_speaker( context, bgp_speaker_id) fip_routes = self._get_central_fip_host_routes_by_bgp_speaker( context, bgp_speaker_id) dvr_fip_routes = self._get_dvr_fip_host_routes_by_bgp_speaker( context, bgp_speaker_id) dvr_fixedip_routes = self._get_dvr_fixed_ip_routes_by_bgp_speaker( context, bgp_speaker_id) return itertools.chain(fip_routes, net_routes, dvr_fip_routes, dvr_fixedip_routes) def get_routes_by_bgp_speaker_binding(self, context, bgp_speaker_id, network_id): """Get all routes for the given bgp_speaker binding.""" with db_api.CONTEXT_READER.using(context): fip_routes = self._get_central_fip_host_routes_by_binding( context, network_id, bgp_speaker_id) net_routes = self._get_tenant_network_routes_by_binding( context, network_id, bgp_speaker_id) dvr_fip_routes = self._get_dvr_fip_host_routes_by_binding( context, network_id, bgp_speaker_id) return itertools.chain(fip_routes, net_routes, dvr_fip_routes) def _get_routes_by_router(self, context, router_id): bgp_speaker_ids = self._get_bgp_speaker_ids_by_router(context, router_id) route_dict = {} for bgp_speaker_id in bgp_speaker_ids: fip_routes = self._get_central_fip_host_routes_by_router( context, router_id, bgp_speaker_id) net_routes = self._get_tenant_network_routes_by_router( context, router_id, bgp_speaker_id) dvr_fip_routes = self._get_dvr_fip_host_routes_by_router( context, router_id, bgp_speaker_id) routes = itertools.chain(fip_routes, net_routes, dvr_fip_routes) route_dict[bgp_speaker_id] = list(routes) return route_dict def _get_central_fip_host_routes_by_router(self, context, router_id, bgp_speaker_id): """Get floating IP host routes with the given router as nexthop.""" with db_api.CONTEXT_READER.using(context): dest_alias = aliased(l3_db.FloatingIP, name='destination') next_hop_alias = aliased(models_v2.IPAllocation, name='next_hop') binding_alias = aliased(BgpSpeakerNetworkBinding, name='binding') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') query = context.session.query(dest_alias.floating_ip_address, next_hop_alias.ip_address) query = query.join( next_hop_alias, next_hop_alias.network_id == dest_alias.floating_network_id) query = query.join(l3_db.Router, dest_alias.router_id == l3_db.Router.id) query = query.filter( l3_db.Router.id == router_id, dest_alias.router_id == l3_db.Router.id, l3_db.Router.id == router_attrs.router_id, router_attrs.distributed == sa.sql.false(), l3_db.Router.gw_port_id == next_hop_alias.port_id, next_hop_alias.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4, binding_alias.network_id == models_v2.Subnet.network_id, binding_alias.bgp_speaker_id == bgp_speaker_id, binding_alias.ip_version == 4, BgpSpeaker.advertise_floating_ip_host_routes == sa.sql.true()) query = query.outerjoin(router_attrs, l3_db.Router.id == router_attrs.router_id) query = query.filter(router_attrs.distributed != sa.sql.true()) return self._host_route_list_from_tuples(query.all()) def _get_dvr_fip_host_routes_by_router(self, context, bgp_speaker_id, router_id): with db_api.CONTEXT_READER.using(context): gw_query = self._get_gateway_query(context, bgp_speaker_id) fip_query = self._get_fip_query(context, bgp_speaker_id) fip_query.filter(l3_db.FloatingIP.router_id == router_id) #Create the join query join_query = self._join_fip_by_host_binding_to_agent_gateway( context, fip_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _get_central_fip_host_routes_by_binding(self, context, network_id, bgp_speaker_id): """Get all floating IP host routes for the given network binding.""" with db_api.CONTEXT_READER.using(context): # Query the DB for floating IP's and the IP address of the # gateway port dest_alias = aliased(l3_db.FloatingIP, name='destination') next_hop_alias = aliased(models_v2.IPAllocation, name='next_hop') binding_alias = aliased(BgpSpeakerNetworkBinding, name='binding') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') query = context.session.query(dest_alias.floating_ip_address, next_hop_alias.ip_address) query = query.join( next_hop_alias, next_hop_alias.network_id == dest_alias.floating_network_id) query = query.join( binding_alias, binding_alias.network_id == dest_alias.floating_network_id) query = query.join(l3_db.Router, dest_alias.router_id == l3_db.Router.id) query = query.filter( dest_alias.floating_network_id == network_id, dest_alias.router_id == l3_db.Router.id, l3_db.Router.gw_port_id == next_hop_alias.port_id, next_hop_alias.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4, binding_alias.network_id == models_v2.Subnet.network_id, binding_alias.bgp_speaker_id == BgpSpeaker.id, BgpSpeaker.id == bgp_speaker_id, BgpSpeaker.advertise_floating_ip_host_routes == sa.sql.true()) query = query.outerjoin(router_attrs, l3_db.Router.id == router_attrs.router_id) query = query.filter(router_attrs.distributed != sa.sql.true()) return self._host_route_list_from_tuples(query.all()) def _get_dvr_fip_host_routes_by_binding(self, context, network_id, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): BgpBinding = BgpSpeakerNetworkBinding gw_query = self._get_gateway_query(context, bgp_speaker_id) gw_query.filter(BgpBinding.network_id == network_id) fip_query = self._get_fip_query(context, bgp_speaker_id) fip_query.filter(BgpBinding.network_id == network_id) #Create the join query join_query = self._join_fip_by_host_binding_to_agent_gateway( context, fip_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _get_central_fip_host_routes_by_bgp_speaker(self, context, bgp_speaker_id): """Get all the floating IP host routes advertised by a BgpSpeaker.""" with db_api.CONTEXT_READER.using(context): dest_alias = aliased(l3_db.FloatingIP, name='destination') next_hop_alias = aliased(models_v2.IPAllocation, name='next_hop') speaker_binding = aliased(BgpSpeakerNetworkBinding, name="speaker_network_mapping") router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') query = context.session.query(dest_alias.floating_ip_address, next_hop_alias.ip_address) query = query.select_from(dest_alias, BgpSpeaker, l3_db.Router, models_v2.Subnet) query = query.join( next_hop_alias, next_hop_alias.network_id == dest_alias.floating_network_id) query = query.join( speaker_binding, speaker_binding.network_id == dest_alias.floating_network_id) query = query.join(l3_db.Router, dest_alias.router_id == l3_db.Router.id) query = query.filter( BgpSpeaker.id == bgp_speaker_id, BgpSpeaker.advertise_floating_ip_host_routes, speaker_binding.bgp_speaker_id == BgpSpeaker.id, dest_alias.floating_network_id == speaker_binding.network_id, next_hop_alias.network_id == speaker_binding.network_id, dest_alias.router_id == l3_db.Router.id, l3_db.Router.gw_port_id == next_hop_alias.port_id, next_hop_alias.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4) query = query.outerjoin(router_attrs, router_attrs.router_id == l3_db.Router.id) query = query.filter(router_attrs.distributed != sa.sql.true()) return self._host_route_list_from_tuples(query.all()) def _get_gateway_query(self, context, bgp_speaker_id): BgpBinding = BgpSpeakerNetworkBinding AddressScope = address_scope_db.AddressScope ML2PortBinding = ml2_models.PortBinding IpAllocation = models_v2.IPAllocation Port = models_v2.Port gw_query = context.session.query( Port.network_id, ML2PortBinding.host, IpAllocation.ip_address, AddressScope.id.label('address_scope_id')) #Subquery for FIP agent gateway ports gw_query = gw_query.filter( ML2PortBinding.port_id == Port.id, IpAllocation.port_id == Port.id, IpAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.ip_version == 4, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == AddressScope.id, Port.network_id == models_v2.Subnet.network_id, Port.device_owner == lib_consts.DEVICE_OWNER_AGENT_GW, Port.network_id == BgpBinding.network_id, BgpBinding.bgp_speaker_id == bgp_speaker_id, BgpBinding.ip_version == 4) return gw_query def _get_fip_query(self, context, bgp_speaker_id): BgpBinding = BgpSpeakerNetworkBinding ML2PortBinding = ml2_models.PortBinding #Subquery for floating IP's fip_query = context.session.query( l3_db.FloatingIP.floating_network_id, ML2PortBinding.host, l3_db.FloatingIP.floating_ip_address) fip_query = fip_query.filter( l3_db.FloatingIP.fixed_port_id == ML2PortBinding.port_id, l3_db.FloatingIP.floating_network_id == BgpBinding.network_id, BgpBinding.bgp_speaker_id == bgp_speaker_id) return fip_query def _get_dvr_fixed_ip_query(self, context, bgp_speaker_id): AddressScope = address_scope_db.AddressScope ML2PortBinding = ml2_models.PortBinding Port = models_v2.Port IpAllocation = models_v2.IPAllocation fixed_ip_query = context.session.query( ML2PortBinding.host, IpAllocation.ip_address, IpAllocation.subnet_id, AddressScope.id.label('address_scope_id')) fixed_ip_query = fixed_ip_query.filter( Port.id == ML2PortBinding.port_id, IpAllocation.port_id == Port.id, Port.device_owner.startswith( lib_consts.DEVICE_OWNER_COMPUTE_PREFIX), IpAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, AddressScope.id == models_v2.SubnetPool.address_scope_id) return fixed_ip_query def _get_dvr_fixed_ip_routes_by_bgp_speaker(self, context, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): gw_query = self._get_gateway_query(context, bgp_speaker_id) fixed_query = self._get_dvr_fixed_ip_query(context, bgp_speaker_id) join_query = self._join_fixed_by_host_binding_to_agent_gateway( context, fixed_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _join_fixed_by_host_binding_to_agent_gateway(self, context, fixed_subq, gw_subq): join_query = context.session.query(fixed_subq.c.ip_address, gw_subq.c.ip_address) and_cond = and_( gw_subq.c.host == fixed_subq.c.host, gw_subq.c.address_scope_id == fixed_subq.c.address_scope_id) return join_query.join(gw_subq, and_cond) def _get_dvr_fip_host_routes_by_bgp_speaker(self, context, bgp_speaker_id): router_attrs = l3_attrs_db.RouterExtraAttributes with db_api.CONTEXT_READER.using(context): gw_query = self._get_gateway_query(context, bgp_speaker_id) fip_query = self._get_fip_query(context, bgp_speaker_id) fip_query = fip_query.filter( l3_db.FloatingIP.router_id == router_attrs.router_id, router_attrs.distributed == sa.sql.true()) #Create the join query join_query = self._join_fip_by_host_binding_to_agent_gateway( context, fip_query.subquery(), gw_query.subquery()) return self._host_route_list_from_tuples(join_query.all()) def _join_fip_by_host_binding_to_agent_gateway(self, context, fip_subq, gw_subq): join_query = context.session.query(fip_subq.c.floating_ip_address, gw_subq.c.ip_address) and_cond = and_( gw_subq.c.host == fip_subq.c.host, gw_subq.c.network_id == fip_subq.c.floating_network_id) return join_query.join(gw_subq, and_cond) def _get_tenant_network_routes_by_binding(self, context, network_id, bgp_speaker_id): """Get all tenant network routes for the given network.""" with db_api.CONTEXT_READER.using(context): tenant_networks_query = self._tenant_networks_by_network_query( context, network_id, bgp_speaker_id) nexthops_query = self._nexthop_ip_addresses_by_binding_query( context, network_id, bgp_speaker_id) join_q = self._join_tenant_networks_to_next_hops( context, tenant_networks_query.subquery(), nexthops_query.subquery()) return self._make_advertised_routes_list(join_q.all()) def _get_tenant_network_routes_by_router(self, context, router_id, bgp_speaker_id): """Get all tenant network routes with the given router as nexthop.""" with db_api.CONTEXT_READER.using(context): scopes = self._get_address_scope_ids_for_bgp_speaker( context, bgp_speaker_id) address_scope = aliased(address_scope_db.AddressScope) inside_query = context.session.query( models_v2.Subnet.cidr, models_v2.IPAllocation.ip_address, address_scope.id) outside_query = context.session.query( address_scope.id, models_v2.IPAllocation.ip_address) speaker_binding = aliased(BgpSpeakerNetworkBinding, name="speaker_network_mapping") port_alias = aliased(l3_db.RouterPort, name='routerport') inside_query = inside_query.filter( port_alias.router_id == router_id, models_v2.IPAllocation.port_id == port_alias.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, address_scope.id.in_(scopes), port_alias.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW, speaker_binding.bgp_speaker_id == bgp_speaker_id) outside_query = outside_query.filter( port_alias.router_id == router_id, port_alias.port_type == lib_consts.DEVICE_OWNER_ROUTER_GW, models_v2.IPAllocation.port_id == port_alias.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, address_scope.id.in_(scopes), speaker_binding.bgp_speaker_id == bgp_speaker_id, speaker_binding.network_id == models_v2.Port.network_id, port_alias.port_id == models_v2.Port.id) inside_query = inside_query.subquery() outside_query = outside_query.subquery() join_query = context.session.query(inside_query.c.cidr, outside_query.c.ip_address) and_cond = and_(inside_query.c.id == outside_query.c.id) join_query = join_query.join(outside_query, and_cond) return self._make_advertised_routes_list(join_query.all()) def _get_tenant_network_routes_by_bgp_speaker(self, context, bgp_speaker_id): """Get all tenant network routes to be advertised by a BgpSpeaker.""" with db_api.CONTEXT_READER.using(context): tenant_nets_q = self._tenant_networks_by_bgp_speaker_query( context, bgp_speaker_id) nexthops_q = self._nexthop_ip_addresses_by_bgp_speaker_query( context, bgp_speaker_id) join_q = self._join_tenant_networks_to_next_hops( context, tenant_nets_q.subquery(), nexthops_q.subquery()) return self._make_advertised_routes_list(join_q.all()) def _join_tenant_networks_to_next_hops(self, context, tenant_networks_subquery, nexthops_subquery): """Join subquery for tenant networks to subquery for nexthop IP's""" left_subq = tenant_networks_subquery right_subq = nexthops_subquery join_query = context.session.query(left_subq.c.cidr, right_subq.c.ip_address) and_cond = and_(left_subq.c.router_id == right_subq.c.router_id, left_subq.c.ip_version == right_subq.c.ip_version) join_query = join_query.join(right_subq, and_cond) return join_query def _tenant_networks_by_network_query(self, context, network_id, bgp_speaker_id): """Return subquery for tenant networks by binding network ID""" address_scope = aliased(address_scope_db.AddressScope, name='address_scope') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') tenant_networks_query = context.session.query( l3_db.RouterPort.router_id, models_v2.Subnet.cidr, models_v2.Subnet.ip_version, address_scope.id) tenant_networks_query = tenant_networks_query.filter( l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW, l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_SNAT, l3_db.RouterPort.router_id == router_attrs.router_id, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.network_id != network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, BgpSpeaker.id == bgp_speaker_id, BgpSpeaker.ip_version == address_scope.ip_version, models_v2.Subnet.ip_version == address_scope.ip_version) return tenant_networks_query def _tenant_networks_by_bgp_speaker_query(self, context, bgp_speaker_id): """Return subquery for tenant networks by binding bgp_speaker_id""" router_id = l3_db.RouterPort.router_id.distinct().label('router_id') tenant_nets_subq = context.session.query(router_id, models_v2.Subnet.cidr, models_v2.Subnet.ip_version) scopes = self._get_address_scope_ids_for_bgp_speaker(context, bgp_speaker_id) filters = self._tenant_networks_by_bgp_speaker_filters(scopes) tenant_nets_subq = tenant_nets_subq.filter(*filters) return tenant_nets_subq def _tenant_networks_by_bgp_speaker_filters(self, address_scope_ids): """Return the filters for querying tenant networks by BGP speaker""" router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') return [models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, l3_db.RouterPort.router_id == router_attrs.router_id, l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_GW, l3_db.RouterPort.port_type != lib_consts.DEVICE_OWNER_ROUTER_SNAT, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, models_v2.Subnet.network_id != BgpSpeakerNetworkBinding.network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id.in_(address_scope_ids), models_v2.Subnet.ip_version == BgpSpeakerNetworkBinding.ip_version, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id, BgpSpeaker.advertise_tenant_networks == sa.sql.true()] def _nexthop_ip_addresses_by_binding_query(self, context, network_id, bgp_speaker_id): """Return the subquery for locating nexthops by binding network""" nexthops_query = context.session.query( l3_db.RouterPort.router_id, models_v2.IPAllocation.ip_address, models_v2.Subnet.ip_version) filters = self._next_hop_ip_addresses_by_binding_filters( network_id, bgp_speaker_id) nexthops_query = nexthops_query.filter(*filters) return nexthops_query def _next_hop_ip_addresses_by_binding_filters(self, network_id, bgp_speaker_id): """Return the filters for querying nexthops by binding network""" address_scope = aliased(address_scope_db.AddressScope, name='address_scope') return [models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id, BgpSpeaker.id == bgp_speaker_id, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id, BgpSpeakerNetworkBinding.network_id == network_id, models_v2.Subnet.network_id == BgpSpeakerNetworkBinding.network_id, models_v2.Subnet.subnetpool_id == models_v2.SubnetPool.id, models_v2.SubnetPool.address_scope_id == address_scope.id, models_v2.Subnet.ip_version == address_scope.ip_version, l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_GW] def _nexthop_ip_addresses_by_bgp_speaker_query(self, context, bgp_speaker_id): """Return the subquery for locating nexthops by BGP speaker""" nexthops_query = context.session.query( l3_db.RouterPort.router_id, models_v2.IPAllocation.ip_address, models_v2.Subnet.ip_version) filters = self._next_hop_ip_addresses_by_bgp_speaker_filters( bgp_speaker_id) nexthops_query = nexthops_query.filter(*filters) return nexthops_query def _next_hop_ip_addresses_by_bgp_speaker_filters(self, bgp_speaker_id): """Return the filters for querying nexthops by BGP speaker""" router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') return [l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_GW, l3_db.RouterPort.router_id == router_attrs.router_id, BgpSpeakerNetworkBinding.network_id == models_v2.Subnet.network_id, BgpSpeakerNetworkBinding.ip_version == models_v2.Subnet.ip_version, BgpSpeakerNetworkBinding.bgp_speaker_id == bgp_speaker_id, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id, models_v2.IPAllocation.subnet_id == models_v2.Subnet.id] def _tenant_prefixes_by_router(self, context, router_id, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): query = context.session.query(models_v2.Subnet.cidr.distinct()) filters = self._tenant_prefixes_by_router_filters(router_id, bgp_speaker_id) query = query.filter(*filters) return [x[0] for x in query.all()] def _tenant_prefixes_by_router_filters(self, router_id, bgp_speaker_id): binding = aliased(BgpSpeakerNetworkBinding, name='network_binding') subnetpool = aliased(models_v2.SubnetPool, name='subnetpool') router_attrs = aliased(l3_attrs_db.RouterExtraAttributes, name='router_attrs') return [models_v2.Subnet.id == models_v2.IPAllocation.subnet_id, models_v2.Subnet.subnetpool_id == subnetpool.id, l3_db.RouterPort.router_id == router_id, l3_db.Router.id == l3_db.RouterPort.router_id, l3_db.Router.id == router_attrs.router_id, l3_db.Router.gw_port_id == models_v2.Port.id, models_v2.Port.network_id == binding.network_id, binding.bgp_speaker_id == BgpSpeaker.id, l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id] def _tenant_prefixes_by_router_interface(self, context, router_port_id, bgp_speaker_id): with db_api.CONTEXT_READER.using(context): query = context.session.query(models_v2.Subnet.cidr.distinct()) filters = self._tenant_prefixes_by_router_filters(router_port_id, bgp_speaker_id) query = query.filter(*filters) return [x[0] for x in query.all()] def _tenant_prefixes_by_router_port_filters(self, router_port_id, bgp_speaker_id): binding = aliased(BgpSpeakerNetworkBinding, name='network_binding') return [models_v2.Subnet.id == models_v2.IPAllocation.subnet_id, l3_db.RouterPort.port_id == router_port_id, l3_db.Router.id == l3_db.RouterPort.router_id, l3_db.Router.gw_port_id == models_v2.Port.id, models_v2.Port.network_id == binding.network_id, binding.bgp_speaker_id == BgpSpeaker.id, models_v2.Subnet.ip_version == binding.ip_version, l3_db.RouterPort.port_type == DEVICE_OWNER_ROUTER_INTF, models_v2.IPAllocation.port_id == l3_db.RouterPort.port_id] def _bgp_speakers_for_gateway_network(self, context, network_id): """Return all BgpSpeakers for the given gateway network""" with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpSpeaker) query = query.filter( BgpSpeakerNetworkBinding.network_id == network_id, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id) return query.all() def _bgp_speakers_for_gw_network_by_family(self, context, network_id, ip_version): """Return the BgpSpeaker by given gateway network and ip_version""" with db_api.CONTEXT_READER.using(context): query = context.session.query(BgpSpeaker) query = query.filter( BgpSpeakerNetworkBinding.network_id == network_id, BgpSpeakerNetworkBinding.bgp_speaker_id == BgpSpeaker.id, BgpSpeakerNetworkBinding.ip_version == ip_version) return query.all() def _make_advertised_routes_list(self, routes): route_list = ({'destination': x, 'next_hop': y} for x, y in routes) return route_list def _route_list_from_prefixes_and_next_hop(self, routes, next_hop): route_list = [{'destination': x, 'next_hop': next_hop} for x in routes] return route_list def _host_route_list_from_tuples(self, ip_next_hop_tuples): """Return the list of host routes given a list of (IP, nexthop)""" return ({'destination': x + '/32', 'next_hop': y} for x, y in ip_next_hop_tuples) def _get_router(self, context, router_id): try: router = model_query.get_by_id(context, l3_db.Router, router_id) except sa_exc.NoResultFound: raise l3_exc.RouterNotFound(router_id=router_id) return router def _get_fip_next_hop(self, context, router_id, ip_address=None): router = self._get_router(context, router_id) gw_port = router.gw_port if not gw_port: return if l3_dvr_db.is_distributed_router(router) and ip_address: return self._get_dvr_fip_next_hop(context, ip_address) for fixed_ip in gw_port.fixed_ips: addr = netaddr.IPAddress(fixed_ip.ip_address) if addr.version == 4: return fixed_ip.ip_address def _get_dvr_fip_agent_gateway_query(self, context): ML2PortBinding = ml2_models.PortBinding IpAllocation = models_v2.IPAllocation Port = models_v2.Port base_query = context.session.query(Port.network_id, ML2PortBinding.host, IpAllocation.ip_address) gw_query = base_query.filter( ML2PortBinding.port_id == Port.id, IpAllocation.port_id == Port.id, Port.device_owner == lib_consts.DEVICE_OWNER_AGENT_GW) return gw_query def _get_fip_fixed_port_host_query(self, context, fip_address): ML2PortBinding = ml2_models.PortBinding fip_query = context.session.query( l3_db.FloatingIP.floating_network_id, ML2PortBinding.host, l3_db.FloatingIP.floating_ip_address) fip_query = fip_query.filter( l3_db.FloatingIP.fixed_port_id == ML2PortBinding.port_id, l3_db.FloatingIP.floating_ip_address == fip_address) return fip_query def _get_dvr_fip_next_hop(self, context, fip_address): try: dvr_agent_gw_query = self._get_dvr_fip_agent_gateway_query( context) fip_fix_port_query = self._get_fip_fixed_port_host_query( context, fip_address) q = self._join_fip_by_host_binding_to_agent_gateway( context, fip_fix_port_query.subquery(), dvr_agent_gw_query.subquery()).one() return q[1] except sa_exc.NoResultFound: return except sa_exc.MultipleResultsFound: return def get_external_networks_for_port(self, ctx, port, match_address_scopes=True): with db_api.CONTEXT_READER.using(ctx): # Retrieve address scope info for the supplied port port_fixed_ips = port.get('fixed_ips') if not port_fixed_ips: return [] subnets_filter = {'id': [x['subnet_id'] for x in port_fixed_ips]} port_subnets = subnet_obj.Subnet.get_objects(ctx, **subnets_filter) port_subnetpools = subnetpool_obj.SubnetPool.get_objects( ctx, id=[x.subnetpool_id for x in port_subnets]) port_scopes = set([x.address_scope_id for x in port_subnetpools]) if match_address_scopes and len(port_scopes) == 0: return [] # Get all router IDs with an interface on the given port's network router_iface_filters = {'device_owner': [DEVICE_OWNER_ROUTER_INTF, DEVICE_OWNER_DVR_INTERFACE], 'network_id': port['network_id']} router_ids = [x.device_id for x in ports.Port.get_objects( ctx, **router_iface_filters)] # Retrieve the gateway ports for the identified routers gw_port_filters = {'device_owner': DEVICE_OWNER_ROUTER_GW, 'device_id': router_ids} gw_ports = ports.Port.get_objects(ctx, **gw_port_filters) # If we don't need to match address scopes, return here if not match_address_scopes: return list(set([x.network_id for x in gw_ports])) # Retrieve address scope info for associated gateway networks gw_fixed_ips = [] for gw_port in gw_ports: gw_fixed_ips.extend(gw_port.fixed_ips) gw_subnet_filters = {'id': [x.subnet_id for x in gw_fixed_ips]} gw_subnets = subnet_obj.Subnet.get_objects(ctx, **gw_subnet_filters) ext_net_subnetpool_map = {} for gw_subnet in gw_subnets: ext_net_id = gw_subnet.network_id ext_pool = subnetpool_obj.SubnetPool.get_object( ctx, id=gw_subnet.subnetpool_id) ext_scope_set = ext_net_subnetpool_map.get(ext_net_id, set()) ext_scope_set.add(ext_pool.address_scope_id) ext_net_subnetpool_map[ext_net_id] = ext_scope_set ext_nets = [] # Match address scopes between port and gateway network(s) for net in ext_net_subnetpool_map.keys(): ext_scopes = ext_net_subnetpool_map[net] if ext_scopes.issubset(port_scopes): ext_nets.append(net) return ext_nets ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/bgp_dragentscheduler_db.py0000664000175000017500000002733400000000000031152 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context as ncontext from neutron_lib.db import api as db_api from neutron_lib.db import model_base from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging import sqlalchemy as sa from sqlalchemy import orm from sqlalchemy.orm import exc from neutron.db import agentschedulers_db as as_db from neutron.db.models import agent as agent_model from neutron_dynamic_routing._i18n import _ from neutron_dynamic_routing._i18n import _LW from neutron_dynamic_routing.extensions import bgp_dragentscheduler as bgp_dras_ext # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts LOG = logging.getLogger(__name__) BGP_DRAGENT_SCHEDULER_OPTS = [ cfg.StrOpt( 'bgp_drscheduler_driver', default='neutron_dynamic_routing.services.bgp.scheduler' '.bgp_dragent_scheduler.ChanceScheduler', help=_('Driver used for scheduling BGP speakers to BGP DrAgent')) ] cfg.CONF.register_opts(BGP_DRAGENT_SCHEDULER_OPTS) class BgpSpeakerDrAgentBinding(model_base.BASEV2): """Represents a mapping between BGP speaker and BGP DRAgent""" __tablename__ = 'bgp_speaker_dragent_bindings' bgp_speaker_id = sa.Column(sa.String(length=36), sa.ForeignKey("bgp_speakers.id", ondelete='CASCADE'), nullable=False) dragent = orm.relation(agent_model.Agent) agent_id = sa.Column(sa.String(length=36), sa.ForeignKey("agents.id", ondelete='CASCADE'), primary_key=True) class BgpDrAgentSchedulerDbMixin(bgp_dras_ext.BgpDrSchedulerPluginBase, as_db.AgentSchedulerDbMixin): bgp_drscheduler = None def add_periodic_dragent_status_check(self): if self.bgp_drscheduler: self.add_agent_status_check_worker( self.remove_bgp_speaker_from_down_dragents) self.add_agent_status_check_worker( self.schedule_all_unscheduled_bgp_speakers) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def schedule_all_unscheduled_bgp_speakers(self): context = ncontext.get_admin_context() if self.bgp_drscheduler: return self.bgp_drscheduler.schedule_all_unscheduled_bgp_speakers( context) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def schedule_unscheduled_bgp_speakers(self, context, host): if self.bgp_drscheduler: return self.bgp_drscheduler.schedule_unscheduled_bgp_speakers( context, host) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def schedule_bgp_speaker(self, context, created_bgp_speaker): if self.bgp_drscheduler: agents = self.bgp_drscheduler.schedule(self, context, created_bgp_speaker) for agent in agents: self._bgp_rpc.bgp_speaker_created(context, created_bgp_speaker['id'], agent.host) else: LOG.warning(_LW("Cannot schedule BgpSpeaker to DrAgent. " "Reason: No scheduler registered.")) def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id): """Associate a BgpDrAgent with a BgpSpeaker.""" try: self._save_bgp_speaker_dragent_binding(context, agent_id, speaker_id) except db_exc.DBDuplicateEntry: raise bgp_dras_ext.DrAgentAssociationError( agent_id=agent_id) LOG.debug('BgpSpeaker %(bgp_speaker_id)s added to ' 'BgpDrAgent %(agent_id)s', {'bgp_speaker_id': speaker_id, 'agent_id': agent_id}) def _save_bgp_speaker_dragent_binding(self, context, agent_id, speaker_id): with db_api.CONTEXT_WRITER.using(context): agent_db = self._get_agent(context, agent_id) agent_up = agent_db['admin_state_up'] is_agent_bgp = (agent_db['agent_type'] == bgp_consts.AGENT_TYPE_BGP_ROUTING) if not is_agent_bgp or not agent_up: raise bgp_dras_ext.DrAgentInvalid(id=agent_id) binding = BgpSpeakerDrAgentBinding() binding.bgp_speaker_id = speaker_id binding.agent_id = agent_id context.session.add(binding) self._bgp_rpc.bgp_speaker_created(context, speaker_id, agent_db.host) def remove_bgp_speaker_from_down_dragents(self): self.reschedule_resources_from_down_agents( agent_type=bgp_consts.AGENT_TYPE_BGP_ROUTING, get_down_bindings=self.get_down_bgp_speaker_bindings, agent_id_attr='agent_id', resource_id_attr='bgp_speaker_id', resource_name='bgp_speaker', reschedule_resource=self.reschedule_bgp_speaker, rescheduling_failed=bgp_dras_ext.BgpSpeakerRescheduleError) def get_down_bgp_speaker_bindings(self, context, agent_dead_limit): cutoff = self.get_cutoff_time(agent_dead_limit) query = ( context.session.query(BgpSpeakerDrAgentBinding). join(agent_model.Agent). filter(agent_model.Agent.heartbeat_timestamp < cutoff, agent_model.Agent.admin_state_up)) down_bindings = [b for b in query] return down_bindings def reschedule_bgp_speaker(self, context, bgp_speaker_id): dragent = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id])[0] bgp_speaker = self.get_bgp_speaker(context, bgp_speaker_id) dragent_id = dragent.id with db_api.CONTEXT_WRITER.using(context): self._remove_bgp_speaker_from_dragent( context, dragent_id, bgp_speaker_id) self.schedule_bgp_speaker(context, bgp_speaker) new_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) if new_dragents == [] or new_dragents[0].id == dragent.id: raise bgp_dras_ext.BgpSpeakerRescheduleError( bgp_speaker_id=bgp_speaker_id, failure_reason="no eligible dr agent found") def _remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): with db_api.CONTEXT_WRITER.using(context): agent_db = self._get_agent(context, agent_id) is_agent_bgp = (agent_db['agent_type'] == bgp_consts.AGENT_TYPE_BGP_ROUTING) if not is_agent_bgp: raise bgp_dras_ext.DrAgentInvalid(id=agent_id) query = context.session.query(BgpSpeakerDrAgentBinding) query = query.filter_by(bgp_speaker_id=speaker_id, agent_id=agent_id) num_deleted = query.delete() if not num_deleted: raise bgp_dras_ext.DrAgentNotHostingBgpSpeaker( bgp_speaker_id=speaker_id, agent_id=agent_id) LOG.debug('BgpSpeaker %(bgp_speaker_id)s removed from ' 'BgpDrAgent %(agent_id)s', {'bgp_speaker_id': speaker_id, 'agent_id': agent_id}) def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): self._remove_bgp_speaker_from_dragent(context, agent_id, speaker_id) agent_db = self._get_agent(context, agent_id) self._bgp_rpc.bgp_speaker_removed(context, speaker_id, agent_db.host) def get_dragents_hosting_bgp_speakers(self, context, bgp_speaker_ids, active=None, admin_state_up=None): query = context.session.query(BgpSpeakerDrAgentBinding) query = query.options(orm.contains_eager( BgpSpeakerDrAgentBinding.dragent)) query = query.join(BgpSpeakerDrAgentBinding.dragent) if len(bgp_speaker_ids) == 1: query = query.filter( BgpSpeakerDrAgentBinding.bgp_speaker_id == ( bgp_speaker_ids[0])) elif bgp_speaker_ids: query = query.filter( BgpSpeakerDrAgentBinding.bgp_speaker_id in bgp_speaker_ids) if admin_state_up is not None: query = query.filter(agent_model.Agent.admin_state_up == admin_state_up) return [binding.dragent for binding in query if as_db.AgentSchedulerDbMixin.is_eligible_agent( active, binding.dragent)] def get_dragent_bgp_speaker_bindings(self, context): return context.session.query(BgpSpeakerDrAgentBinding).all() def list_dragent_hosting_bgp_speaker(self, context, speaker_id): dragents = self.get_dragents_hosting_bgp_speakers(context, [speaker_id]) agent_ids = [dragent.id for dragent in dragents] if not agent_ids: return {'agents': []} return {'agents': self.get_agents(context, filters={'id': agent_ids})} def list_bgp_speaker_on_dragent(self, context, agent_id): query = context.session.query(BgpSpeakerDrAgentBinding.bgp_speaker_id) query = query.filter_by(agent_id=agent_id) bgp_speaker_ids = [item[0] for item in query] if not bgp_speaker_ids: # Exception will be thrown if the requested agent does not exist. self._get_agent(context, agent_id) return {'bgp_speakers': []} return {'bgp_speakers': self.get_bgp_speakers(context, filters={'id': bgp_speaker_ids})} def get_bgp_speakers_for_agent_host(self, context, host): agent = self._get_agent_by_type_and_host( context, bgp_consts.AGENT_TYPE_BGP_ROUTING, host) if not agent.admin_state_up: return {} query = context.session.query(BgpSpeakerDrAgentBinding) query = query.filter(BgpSpeakerDrAgentBinding.agent_id == agent.id) try: binding = query.one() except exc.NoResultFound: return [] bgp_speaker = self.get_bgp_speaker_with_advertised_routes( context, binding['bgp_speaker_id']) return [bgp_speaker] def get_bgp_speaker_by_speaker_id(self, context, bgp_speaker_id): try: return self.get_bgp_speaker(context, bgp_speaker_id) except exc.NoResultFound: return {} def get_bgp_peer_by_peer_id(self, context, bgp_peer_id): try: return self.get_bgp_peer(context, bgp_peer_id) except exc.NoResultFound: return {} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/0000775000175000017500000000000000000000000025740 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/README0000664000175000017500000000020300000000000026613 0ustar00zuulzuul00000000000000For details refer to: https://docs.openstack.org/neutron/latest/contributor/alembic_migrations.html#independent-sub-project-tables ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/__init__.py0000664000175000017500000000000000000000000030037 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/0000775000175000017500000000000000000000000031570 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/__init__.py0000664000175000017500000000000000000000000033667 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/env.py0000664000175000017500000000500000000000000032725 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from logging import config as logging_config from alembic import context from neutron_lib.db import model_base from oslo_config import cfg from oslo_db.sqlalchemy import session import sqlalchemy as sa from sqlalchemy import event from neutron_dynamic_routing.db.migration.models import head # noqa MYSQL_ENGINE = None DR_VERSION_TABLE = 'alembic_version_dr' config = context.config neutron_config = config.neutron_config logging_config.fileConfig(config.config_file_name) target_metadata = model_base.BASEV2.metadata def set_mysql_engine(): try: mysql_engine = neutron_config.command.mysql_engine except cfg.NoSuchOptError: mysql_engine = None global MYSQL_ENGINE MYSQL_ENGINE = (mysql_engine or model_base.BASEV2.__table_args__['mysql_engine']) def run_migrations_offline(): set_mysql_engine() kwargs = dict() if neutron_config.database.connection: kwargs['url'] = neutron_config.database.connection else: kwargs['dialect_name'] = neutron_config.database.engine kwargs['version_table'] = DR_VERSION_TABLE context.configure(**kwargs) with context.begin_transaction(): context.run_migrations() @event.listens_for(sa.Table, 'after_parent_attach') def set_storage_engine(target, parent): if MYSQL_ENGINE: target.kwargs['mysql_engine'] = MYSQL_ENGINE def run_migrations_online(): set_mysql_engine() engine = session.create_engine(neutron_config.database.connection) connection = engine.connect() context.configure( connection=connection, target_metadata=target_metadata, version_table=DR_VERSION_TABLE ) try: with context.begin_transaction(): context.run_migrations() finally: connection.close() engine.dispose() if context.is_offline_mode(): run_migrations_offline() else: run_migrations_online() ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/script.py.mako 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/script.py.mak0000664000175000017500000000203300000000000034213 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """${message} Revision ID: ${up_revision} Revises: ${down_revision} Create Date: ${create_date} """ # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} % if branch_labels: branch_labels = ${repr(branch_labels)} %endif from alembic import op import sqlalchemy as sa ${imports if imports else ""} def upgrade(): ${upgrades if upgrades else "pass"} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/0000775000175000017500000000000000000000000033440 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000119 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/CONTRACT_HEAD 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/CONT0000664000175000017500000000001500000000000034122 0ustar00zuulzuul00000000000000a589fdb5724c ././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/EXPAND_HEAD 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/EXPA0000664000175000017500000000001500000000000034114 0ustar00zuulzuul00000000000000f399fa0f5f25 ././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000113 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/ 28 mtime=1648641159.3846602 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000775000175000017500000000000000000000000034336 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022600000000000011455 xustar0000000000000000122 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/ 28 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000775000175000017500000000000000000000000034336 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000027000000000000011454 xustar0000000000000000162 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/4cf8bc3edb66_rename_tenant_to_project.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000664000175000017500000000636400000000000034351 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """rename tenant to project Revision ID: 4cf8bc3edb66 Revises: 61cc795e43e8 Create Date: 2016-07-14 17:32:00.852342 """ from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection from neutron.db import migration # revision identifiers, used by Alembic. revision = '4cf8bc3edb66' down_revision = '61cc795e43e8' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] _INSPECTOR = None def get_inspector(): """Reuse inspector""" global _INSPECTOR if _INSPECTOR: return _INSPECTOR bind = op.get_bind() _INSPECTOR = reflection.Inspector.from_engine(bind) return _INSPECTOR def get_tables(): """ Returns hardcoded list of tables which have ``tenant_id`` column. The list is hardcoded to match the state of the schema when this upgrade script is run. """ tables = [ 'bgp_peers', 'bgp_speakers', ] return tables def get_columns(table): """Returns list of columns for given table.""" inspector = get_inspector() return inspector.get_columns(table) def get_data(): """Returns combined list of tuples: [(table, column)]. The list is built from tables with a tenant_id column. """ output = [] tables = get_tables() for table in tables: columns = get_columns(table) for column in columns: if column['name'] == 'tenant_id': output.append((table, column)) return output def alter_column(table, column): old_name = 'tenant_id' new_name = 'project_id' op.alter_column( table_name=table, column_name=old_name, new_column_name=new_name, existing_type=column['type'], existing_nullable=column['nullable'] ) def recreate_index(index, table_name): old_name = index['name'] new_name = old_name.replace('tenant', 'project') op.drop_index(op.f(old_name), table_name) op.create_index(new_name, table_name, ['project_id']) def upgrade(): """Code reused from Change-Id: I87a8ef342ccea004731ba0192b23a8e79bc382dc """ inspector = get_inspector() data = get_data() for table, column in data: alter_column(table, column) indexes = inspector.get_indexes(table) for index in indexes: if 'tenant_id' in index['name']: recreate_index(index, table) def contract_creation_exceptions(): """Special migration for the blueprint to support Keystone V3. We drop all tenant_id columns and create project_id columns instead. """ return { sa.Column: ['.'.join([table, 'project_id']) for table in get_tables()], sa.Index: get_tables() } ././@PaxHeader0000000000000000000000000000024700000000000011460 xustar0000000000000000145 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/61cc795e43e8_initial.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000664000175000017500000000172100000000000034341 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """initial Revision ID: 61cc795e43e8 Revises: start_neutron_dynamic_routing Create Date: 2016-05-03 08:30:18.421995 """ from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = '61cc795e43e8' down_revision = 'start_neutron_dynamic_routing' branch_labels = (cli.CONTRACT_BRANCH,) def upgrade(): pass ././@PaxHeader0000000000000000000000000000022400000000000011453 xustar0000000000000000120 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/expand/ 28 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000775000175000017500000000000000000000000034336 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000024500000000000011456 xustar0000000000000000143 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/expand/f399fa0f5f25_initial.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/newt0000664000175000017500000000206100000000000034337 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """initial Revision ID: f399fa0f5f25 Revises: None Create Date: 2016-05-03 08:30:18.421995 """ from neutron.db import migration from neutron.db.migration import cli # revision identifiers, used by Alembic. revision = 'f399fa0f5f25' down_revision = 'start_neutron_dynamic_routing' branch_labels = (cli.EXPAND_BRANCH,) # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] def upgrade(): pass ././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000113 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/ 28 mtime=1648641159.3846602 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/quee0000775000175000017500000000000000000000000034320 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022600000000000011455 xustar0000000000000000122 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/contract/ 28 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/quee0000775000175000017500000000000000000000000034320 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000027000000000000011454 xustar0000000000000000162 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/contract/a589fdb5724c_change_size_of_as_number.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/quee0000664000175000017500000000225600000000000034327 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """change size of as number Revision ID: a589fdb5724c Revises: 4cf8bc3edb66 Create Date: 2017-08-31 13:50:28.324422 """ from alembic import op import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = 'a589fdb5724c' down_revision = '4cf8bc3edb66' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.QUEENS] def upgrade(): op.alter_column('bgp_speakers', 'local_as', nullable=False, type_=sa.BigInteger()) op.alter_column('bgp_peers', 'remote_as', nullable=False, type_=sa.BigInteger()) ././@PaxHeader0000000000000000000000000000024000000000000011451 xustar0000000000000000138 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/start_neutron_dynamic_routing.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/alembic_migrations/versions/star0000664000175000017500000000161600000000000034340 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """start neutron-dynamic-routing chain Revision ID: start_neutron_dynamic_routing Revises: None Create Date: 2016-04-26 18:42:08.262632 """ # revision identifiers, used by Alembic. revision = 'start_neutron_dynamic_routing' down_revision = None def upgrade(): pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/models/0000775000175000017500000000000000000000000027223 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/models/__init__.py0000664000175000017500000000000000000000000031322 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/db/migration/models/head.py0000664000175000017500000000153200000000000030477 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base from neutron_dynamic_routing.db import bgp_db # noqa from neutron_dynamic_routing.db import bgp_dragentscheduler_db # noqa def get_metadata(): return model_base.BASEV2.metadata ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/extensions/0000775000175000017500000000000000000000000025561 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/extensions/__init__.py0000664000175000017500000000000000000000000027660 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/extensions/bgp.py0000664000175000017500000000546500000000000026715 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Development Coompany LP # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # from neutron_lib.api.definitions import bgp as bgp_api_def from neutron_lib.api import extensions as api_ext from neutron_lib import exceptions as n_exc from neutron.api.v2 import resource_helper as rh from neutron_dynamic_routing._i18n import _ # Dynamic Routing Exceptions class BgpSpeakerNotFound(n_exc.NotFound): message = _("BGP speaker %(id)s could not be found.") class BgpPeerNotFound(n_exc.NotFound): message = _("BGP peer %(id)s could not be found.") class BgpPeerNotAuthenticated(n_exc.NotFound): message = _("BGP peer %(bgp_peer_id)s not authenticated.") class BgpSpeakerPeerNotAssociated(n_exc.NotFound): message = _("BGP peer %(bgp_peer_id)s is not associated with " "BGP speaker %(bgp_speaker_id)s.") class BgpSpeakerNetworkNotAssociated(n_exc.NotFound): message = _("Network %(network_id)s is not associated with " "BGP speaker %(bgp_speaker_id)s.") class BgpSpeakerNetworkBindingError(n_exc.Conflict): message = _("Network %(network_id)s is already bound to BgpSpeaker " "%(bgp_speaker_id)s.") class NetworkNotBound(n_exc.NotFound): message = _("Network %(network_id)s is not bound to a BgpSpeaker.") class DuplicateBgpPeerIpException(n_exc.Conflict): message = _("BGP Speaker %(bgp_speaker_id)s is already configured to " "peer with a BGP Peer at %(peer_ip)s, it cannot peer with " "BGP Peer %(bgp_peer_id)s.") class InvalidBgpPeerMd5Authentication(n_exc.BadRequest): message = _("A password must be supplied when using auth_type md5.") class NetworkNotBoundForIpVersion(NetworkNotBound): message = _("Network %(network_id)s is not bound to a IPv%(ip_version)s " "BgpSpeaker.") class Bgp(api_ext.APIExtensionDescriptor): api_definition = bgp_api_def @classmethod def get_resources(cls): plural_mappings = rh.build_plural_mappings( {}, bgp_api_def.RESOURCE_ATTRIBUTE_MAP) exts = rh.build_resource_info(plural_mappings, bgp_api_def.RESOURCE_ATTRIBUTE_MAP, bgp_api_def.ALIAS, action_map=bgp_api_def.ACTION_MAP) return exts ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/extensions/bgp_4byte_asn.py0000664000175000017500000000147000000000000030655 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 bgp_4byte_asn as api_def from neutron_lib.api import extensions as api_extensions class Bgp_4byte_asn(api_extensions.APIExtensionDescriptor): """Extension class supporting bgp 4-byte AS numbers. """ api_definition = api_def ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/extensions/bgp_dragentscheduler.py0000664000175000017500000001251000000000000032305 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc from neutron.api import extensions from neutron.api.v2 import resource from neutron import wsgi from neutron_lib.api.definitions import bgp as bgp_ext from neutron_lib.api.definitions import bgp_dragentscheduler as api_def from neutron_lib.api import extensions as api_extensions from neutron_lib.api import faults from neutron_lib import exceptions as n_exc from neutron_lib.exceptions import agent as agent_exc from neutron_lib.plugins import directory from oslo_log import log as logging import webob from neutron_dynamic_routing._i18n import _, _LE LOG = logging.getLogger(__name__) class DrAgentInvalid(agent_exc.AgentNotFound): message = _("BgpDrAgent %(id)s is invalid or has been disabled.") class DrAgentNotHostingBgpSpeaker(n_exc.NotFound): message = _("BGP speaker %(bgp_speaker_id)s is not hosted " "by the BgpDrAgent %(agent_id)s.") class DrAgentAssociationError(n_exc.Conflict): message = _("BgpDrAgent %(agent_id)s is already associated " "to a BGP speaker.") class BgpSpeakerRescheduleError(n_exc.Conflict): message = _("Failed rescheduling %(bgp_speaker_id)s: " "%(failure_reason)s.") class BgpDrSchedulerController(wsgi.Controller): """Schedule BgpSpeaker for a BgpDrAgent""" def get_plugin(self): plugin = directory.get_plugin(bgp_ext.ALIAS) if not plugin: LOG.error(_LE('No plugin for BGP routing registered')) msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) return plugin def index(self, request, **kwargs): plugin = self.get_plugin() return plugin.list_bgp_speaker_on_dragent( request.context, kwargs['agent_id']) def create(self, request, body, **kwargs): plugin = self.get_plugin() return plugin.add_bgp_speaker_to_dragent( request.context, kwargs['agent_id'], body['bgp_speaker_id']) def delete(self, request, id, **kwargs): plugin = self.get_plugin() return plugin.remove_bgp_speaker_from_dragent( request.context, kwargs['agent_id'], id) class BgpDrAgentController(wsgi.Controller): def get_plugin(self): plugin = directory.get_plugin(bgp_ext.ALIAS) if not plugin: LOG.error(_LE('No plugin for BGP routing registered')) msg = _('The resource could not be found.') raise webob.exc.HTTPNotFound(msg) return plugin def index(self, request, **kwargs): plugin = directory.get_plugin(bgp_ext.ALIAS) return plugin.list_dragent_hosting_bgp_speaker( request.context, kwargs['bgp_speaker_id']) class Bgp_dragentscheduler(api_extensions.APIExtensionDescriptor): """Extension class supporting Dynamic Routing scheduler. """ api_definition = api_def @classmethod def get_resources(cls): """Returns Ext Resources.""" exts = [] parent = dict(member_name="agent", collection_name="agents") controller = resource.Resource(BgpDrSchedulerController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension(api_def.BGP_DRINSTANCES, controller, parent)) parent = dict(member_name="bgp_speaker", collection_name="bgp-speakers") controller = resource.Resource(BgpDrAgentController(), faults.FAULT_MAP) exts.append(extensions.ResourceExtension(api_def.BGP_DRAGENTS, controller, parent)) return exts class BgpDrSchedulerPluginBase(object, metaclass=abc.ABCMeta): """REST API to operate BGP dynamic routing agent scheduler. All the methods must be executed in admin context. """ def get_plugin_description(self): return "Neutron BGP dynamic routing scheduler Plugin" def get_plugin_type(self): return bgp_ext.ALIAS @abc.abstractmethod def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id): pass @abc.abstractmethod def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): pass @abc.abstractmethod def list_dragent_hosting_bgp_speaker(self, context, speaker_id): pass @abc.abstractmethod def list_bgp_speaker_on_dragent(self, context, agent_id): pass @abc.abstractmethod def get_bgp_speakers_for_agent_host(self, context, host): pass @abc.abstractmethod def get_bgp_speaker_by_speaker_id(self, context, speaker_id): pass @abc.abstractmethod def get_bgp_peer_by_peer_id(self, context, bgp_peer_id): pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/policies/0000775000175000017500000000000000000000000025171 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/policies/__init__.py0000664000175000017500000000162000000000000027301 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools from neutron_dynamic_routing.policies import bgp_dragent from neutron_dynamic_routing.policies import bgp_peer from neutron_dynamic_routing.policies import bgp_speaker def list_rules(): return itertools.chain( bgp_speaker.list_rules(), bgp_peer.list_rules(), bgp_dragent.list_rules(), ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/policies/base.py0000664000175000017500000000132300000000000026454 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/policies/bgp_dragent.py0000664000175000017500000000367700000000000030034 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from neutron_dynamic_routing.policies import base rules = [ policy.DocumentedRuleDefault( 'add_bgp_speaker_to_dragent', base.RULE_ADMIN_ONLY, 'Add a BGP speaker to a dynamic routing agent', [ { 'method': 'POST', 'path': '/agents/{agent_id}/bgp-drinstances', }, ] ), policy.DocumentedRuleDefault( 'remove_bgp_speaker_from_dragent', base.RULE_ADMIN_ONLY, 'Remove a BGP speaker from a dynamic routing agent', [ { 'method': 'DELETE', 'path': '/agents/{agent_id}/bgp-drinstances/{bgp_speaker_id}', }, ] ), policy.DocumentedRuleDefault( 'list_bgp_speaker_on_dragent', base.RULE_ADMIN_ONLY, 'List BGP speakers hosted by a dynamic routing agent', [ { 'method': 'GET', 'path': '/agents/{agent_id}/bgp-drinstances', }, ] ), policy.DocumentedRuleDefault( 'list_dragent_hosting_bgp_speaker', base.RULE_ADMIN_ONLY, 'List dynamic routing agents hosting a BGP speaker', [ { 'method': 'GET', 'path': '/bgp-speakers/{bgp_speaker_id}/bgp-dragents', }, ] ), ] def list_rules(): return rules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/policies/bgp_peer.py0000664000175000017500000000337400000000000027335 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from neutron_dynamic_routing.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgp_peer', base.RULE_ADMIN_ONLY, 'Create a BGP peer', [ { 'method': 'POST', 'path': '/bgp-peers', }, ] ), policy.DocumentedRuleDefault( 'update_bgp_peer', base.RULE_ADMIN_ONLY, 'Update a BGP peer', [ { 'method': 'PUT', 'path': '/bgp-peers/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_bgp_peer', base.RULE_ADMIN_ONLY, 'Delete a BGP peer', [ { 'method': 'DELETE', 'path': '/bgp-peers/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgp_peer', base.RULE_ADMIN_ONLY, 'Get BGP peers', [ { 'method': 'GET', 'path': '/bgp-peers', }, { 'method': 'GET', 'path': '/bgp-peers/{id}', }, ] ), ] def list_rules(): return rules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/policies/bgp_speaker.py0000664000175000017500000000636000000000000030032 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_policy import policy from neutron_dynamic_routing.policies import base rules = [ policy.DocumentedRuleDefault( 'create_bgp_speaker', base.RULE_ADMIN_ONLY, 'Create a BGP speaker', [ { 'method': 'POST', 'path': '/bgp-speakers', }, ] ), policy.DocumentedRuleDefault( 'update_bgp_speaker', base.RULE_ADMIN_ONLY, 'Update a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_bgp_speaker', base.RULE_ADMIN_ONLY, 'Delete a BGP speaker', [ { 'method': 'DELETE', 'path': '/bgp-speakers/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_bgp_speaker', base.RULE_ADMIN_ONLY, 'Get BGP speakers', [ { 'method': 'GET', 'path': '/bgp-speakers', }, { 'method': 'GET', 'path': '/bgp-speakers/{id}', }, ] ), policy.DocumentedRuleDefault( 'add_bgp_peer', base.RULE_ADMIN_ONLY, 'Add a BGP peer to a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/add_bgp_peer', }, ] ), policy.DocumentedRuleDefault( 'remove_bgp_peer', base.RULE_ADMIN_ONLY, 'Remove a BGP peer from a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/remove_bgp_peer', }, ] ), policy.DocumentedRuleDefault( 'add_gateway_network', base.RULE_ADMIN_ONLY, 'Add a gateway network to a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/add_gateway_network', }, ] ), policy.DocumentedRuleDefault( 'remove_gateway_network', base.RULE_ADMIN_ONLY, 'Remove a gateway network from a BGP speaker', [ { 'method': 'PUT', 'path': '/bgp-speakers/{id}/remove_gateway_network', }, ] ), policy.DocumentedRuleDefault( 'get_advertised_routes', base.RULE_ADMIN_ONLY, 'Get advertised routes of a BGP speaker', [ { 'method': 'GET', 'path': '/bgp-speakers/{id}/get_advertised_routes', }, ] ), ] def list_rules(): return rules ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/0000775000175000017500000000000000000000000025205 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/__init__.py0000664000175000017500000000000000000000000027304 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/0000775000175000017500000000000000000000000025755 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/__init__.py0000664000175000017500000000000000000000000030054 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/0000775000175000017500000000000000000000000027053 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/__init__.py0000664000175000017500000000000000000000000031152 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/bgp_dragent.py0000664000175000017500000007623500000000000031716 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import collections from neutron_lib.agent import constants as agent_consts from neutron_lib.agent import topics from neutron_lib import context from neutron_lib import rpc as n_rpc from neutron_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from oslo_service import periodic_task from oslo_utils import importutils from neutron.agent import rpc as agent_rpc from neutron.common import utils from neutron import manager from neutron_dynamic_routing.extensions import bgp as bgp_ext from neutron_dynamic_routing._i18n import _, _LE, _LI, _LW from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as driver_exc # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa LOG = logging.getLogger(__name__) class BgpDrAgent(manager.Manager): """BGP Dynamic Routing agent service manager. Note that the public methods of this class are exposed as the server side of an rpc interface. The neutron server uses api.rpc.agentnotifiers.bgp_dr_rpc_agent_api.BgpDrAgentNotifyApi as the client side to execute the methods here. For more information about changing rpc interfaces, see https://docs.openstack.org/neutron/latest/contributor/internals/rpc_api.html. API version history: 1.0 initial Version """ target = oslo_messaging.Target(version='1.0') def __init__(self, host, conf=None): super(BgpDrAgent, self).__init__() self.initialize_driver(conf) self.needs_resync_reasons = collections.defaultdict(list) self.needs_full_sync_reason = None self.cache = BgpSpeakerCache() self.context = context.get_admin_context_without_session() self.plugin_rpc = BgpDrPluginApi(bgp_consts.BGP_PLUGIN, self.context, host) def initialize_driver(self, conf): self.conf = conf or cfg.CONF.BGP try: self.dr_driver_cls = ( importutils.import_object(self.conf.bgp_speaker_driver, self.conf)) except ImportError: LOG.exception(_LE("Error while importing BGP speaker driver %s"), self.conf.bgp_speaker_driver) raise SystemExit(1) def _handle_driver_failure(self, bgp_speaker_id, method, driver_exec): self.schedule_resync(reason=driver_exec, speaker_id=bgp_speaker_id) LOG.error(_LE('Call to driver for BGP Speaker %(bgp_speaker)s ' '%(method)s has failed with exception ' '%(driver_exec)s.'), {'bgp_speaker': bgp_speaker_id, 'method': method, 'driver_exec': driver_exec}) def after_start(self): self.run() LOG.info(_LI("BGP Dynamic Routing agent started")) def run(self): """Activate BGP Dynamic Routing agent.""" self.sync_state(self.context) self.periodic_resync(self.context) @runtime.synchronized('bgp-dragent') def sync_state(self, context, full_sync=None, bgp_speakers=None): try: hosted_bgp_speakers = self.plugin_rpc.get_bgp_speakers(context) hosted_bgp_speaker_ids = [bgp_speaker['id'] for bgp_speaker in hosted_bgp_speakers] cached_bgp_speakers = self.cache.get_bgp_speaker_ids() for bgp_speaker_id in cached_bgp_speakers: if bgp_speaker_id not in hosted_bgp_speaker_ids: self.remove_bgp_speaker_from_dragent(bgp_speaker_id) resync_all = not bgp_speakers or full_sync only_bs = set() if resync_all else set(bgp_speakers) for hosted_bgp_speaker in hosted_bgp_speakers: hosted_bs_id = hosted_bgp_speaker['id'] if resync_all or hosted_bs_id in only_bs: if not self.cache.is_bgp_speaker_added(hosted_bs_id): self.safe_configure_dragent_for_bgp_speaker( hosted_bgp_speaker) continue self.sync_bgp_speaker(hosted_bgp_speaker) resync_reason = "Periodic route cache refresh" self.schedule_resync(speaker_id=hosted_bs_id, reason=resync_reason) except Exception as e: self.schedule_full_resync(reason=e) LOG.error(_LE('Unable to sync BGP speaker state.')) def sync_bgp_speaker(self, bgp_speaker): # sync BGP Speakers bgp_peer_ips = set( [bgp_peer['peer_ip'] for bgp_peer in bgp_speaker['peers']]) cached_bgp_peer_ips = set( self.cache.get_bgp_peer_ips(bgp_speaker['id'])) removed_bgp_peer_ips = cached_bgp_peer_ips - bgp_peer_ips for bgp_peer_ip in removed_bgp_peer_ips: self.remove_bgp_peer_from_bgp_speaker(bgp_speaker['id'], bgp_peer_ip) if bgp_peer_ips: self.add_bgp_peers_to_bgp_speaker(bgp_speaker) # sync advertise routes cached_adv_routes = self.cache.get_adv_routes(bgp_speaker['id']) adv_routes = bgp_speaker['advertised_routes'] if cached_adv_routes == adv_routes: return for cached_route in cached_adv_routes: if cached_route not in adv_routes: self.withdraw_route_via_bgp_speaker(bgp_speaker['id'], bgp_speaker['local_as'], cached_route) self.advertise_routes_via_bgp_speaker(bgp_speaker) @utils.exception_logger() def _periodic_resync_helper(self, context): """Resync the BgpDrAgent state at the configured interval.""" if self.needs_resync_reasons or self.needs_full_sync_reason: full_sync = self.needs_full_sync_reason reasons = self.needs_resync_reasons # Reset old reasons self.needs_full_sync_reason = None self.needs_resync_reasons = collections.defaultdict(list) if full_sync: LOG.debug("resync all: %(reason)s", {"reason": full_sync}) for bgp_speaker, reason in reasons.items(): LOG.debug("resync (%(bgp_speaker)s): %(reason)s", {"reason": reason, "bgp_speaker": bgp_speaker}) self.sync_state( context, full_sync=full_sync, bgp_speakers=reasons.keys()) # NOTE: spacing is set 1 sec. The actual interval is controlled # by neutron/service.py which defaults to CONF.periodic_interval @periodic_task.periodic_task(spacing=1) def periodic_resync(self, context): LOG.debug("Started periodic resync.") self._periodic_resync_helper(context) @runtime.synchronized('bgp-dr-agent') def bgp_speaker_create_end(self, context, payload): """Handle bgp_speaker_create_end notification event.""" bgp_speaker_id = payload['bgp_speaker']['id'] LOG.debug('Received BGP speaker create notification for ' 'speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) self.add_bgp_speaker_helper(bgp_speaker_id) @runtime.synchronized('bgp-dr-agent') def bgp_speaker_remove_end(self, context, payload): """Handle bgp_speaker_remove_end notification event.""" bgp_speaker_id = payload['bgp_speaker']['id'] LOG.debug('Received BGP speaker remove notification for ' 'speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) self.remove_bgp_speaker_from_dragent(bgp_speaker_id) @runtime.synchronized('bgp-dr-agent') def bgp_peer_association_end(self, context, payload): """Handle bgp_peer_association_end notification event.""" bgp_peer_id = payload['bgp_peer']['peer_id'] bgp_speaker_id = payload['bgp_peer']['speaker_id'] LOG.debug('Received BGP peer associate notification for ' 'speaker_id=%(speaker_id)s peer_id=%(peer_id)s ' 'from the neutron server.', {'speaker_id': bgp_speaker_id, 'peer_id': bgp_peer_id}) self.add_bgp_peer_helper(bgp_speaker_id, bgp_peer_id) @runtime.synchronized('bgp-dr-agent') def bgp_peer_disassociation_end(self, context, payload): """Handle bgp_peer_disassociation_end notification event.""" bgp_peer_ip = payload['bgp_peer']['peer_ip'] bgp_speaker_id = payload['bgp_peer']['speaker_id'] LOG.debug('Received BGP peer disassociate notification for ' 'speaker_id=%(speaker_id)s peer_ip=%(peer_ip)s ' 'from the neutron server.', {'speaker_id': bgp_speaker_id, 'peer_ip': bgp_peer_ip}) self.remove_bgp_peer_from_bgp_speaker(bgp_speaker_id, bgp_peer_ip) @runtime.synchronized('bgp-dr-agent') def bgp_routes_advertisement_end(self, context, payload): """Handle bgp_routes_advertisement_end notification event.""" bgp_speaker_id = payload['advertise_routes']['speaker_id'] LOG.debug('Received routes advertisement end notification ' 'for speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) routes = payload['advertise_routes']['routes'] self.add_routes_helper(bgp_speaker_id, routes) @runtime.synchronized('bgp-dr-agent') def bgp_routes_withdrawal_end(self, context, payload): """Handle bgp_routes_withdrawal_end notification event.""" bgp_speaker_id = payload['withdraw_routes']['speaker_id'] LOG.debug('Received route withdrawal notification for ' 'speaker_id=%(speaker_id)s from the neutron server.', {'speaker_id': bgp_speaker_id}) routes = payload['withdraw_routes']['routes'] self.withdraw_routes_helper(bgp_speaker_id, routes) def add_bgp_speaker_helper(self, bgp_speaker_id): """Add BGP speaker.""" bgp_speaker = self.safe_get_bgp_speaker_info(bgp_speaker_id) if bgp_speaker: self.add_bgp_speaker_on_dragent(bgp_speaker) def add_bgp_peer_helper(self, bgp_speaker_id, bgp_peer_id): """Add BGP peer.""" # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return bgp_peer = self.safe_get_bgp_peer_info(bgp_speaker_id, bgp_peer_id) if bgp_peer: bgp_speaker_as = self.cache.get_bgp_speaker_local_as( bgp_speaker_id) self.add_bgp_peer_to_bgp_speaker(bgp_speaker_id, bgp_speaker_as, bgp_peer) def add_routes_helper(self, bgp_speaker_id, routes): """Advertise routes to BGP speaker.""" # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return bgp_speaker_as = self.cache.get_bgp_speaker_local_as(bgp_speaker_id) for route in routes: self.advertise_route_via_bgp_speaker(bgp_speaker_id, bgp_speaker_as, route) if self.is_resync_scheduled(bgp_speaker_id): break def withdraw_routes_helper(self, bgp_speaker_id, routes): """Withdraw routes advertised by BGP speaker.""" # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return bgp_speaker_as = self.cache.get_bgp_speaker_local_as(bgp_speaker_id) for route in routes: self.withdraw_route_via_bgp_speaker(bgp_speaker_id, bgp_speaker_as, route) if self.is_resync_scheduled(bgp_speaker_id): break def safe_get_bgp_speaker_info(self, bgp_speaker_id): try: bgp_speaker = self.plugin_rpc.get_bgp_speaker_info(self.context, bgp_speaker_id) if not bgp_speaker: LOG.warning(_LW('BGP Speaker %s has been deleted.'), bgp_speaker_id) return bgp_speaker except Exception as e: self.schedule_resync(speaker_id=bgp_speaker_id, reason=e) LOG.error(_LE('BGP Speaker %(bgp_speaker)s info call ' 'failed with reason=%(e)s.'), {'bgp_speaker': bgp_speaker_id, 'e': e}) def safe_get_bgp_peer_info(self, bgp_speaker_id, bgp_peer_id): try: bgp_peer = self.plugin_rpc.get_bgp_peer_info(self.context, bgp_peer_id) if not bgp_peer: LOG.warning(_LW('BGP Peer %s has been deleted.'), bgp_peer) return bgp_peer except Exception as e: self.schedule_resync(speaker_id=bgp_speaker_id, reason=e) LOG.error(_LE('BGP peer %(bgp_peer)s info call ' 'failed with reason=%(e)s.'), {'bgp_peer': bgp_peer_id, 'e': e}) @utils.exception_logger() def safe_configure_dragent_for_bgp_speaker(self, bgp_speaker): try: self.add_bgp_speaker_on_dragent(bgp_speaker) except (bgp_ext.BgpSpeakerNotFound, RuntimeError): LOG.warning(_LW('BGP speaker %s may have been deleted and its ' 'resources may have already been disposed.'), bgp_speaker['id']) def add_bgp_speaker_on_dragent(self, bgp_speaker): # Caching BGP speaker details in BGPSpeakerCache. Will be used # during smooth. self.cache.put_bgp_speaker(bgp_speaker) LOG.debug('Calling driver for adding BGP speaker %(speaker_id)s,' ' speaking for local_as %(local_as)s', {'speaker_id': bgp_speaker['id'], 'local_as': bgp_speaker['local_as']}) try: self.dr_driver_cls.add_bgp_speaker(bgp_speaker['local_as']) except driver_exc.BgpSpeakerAlreadyScheduled: return except Exception as e: self._handle_driver_failure(bgp_speaker['id'], 'add_bgp_speaker', e) # Add peer and route information to the driver. self.add_bgp_peers_to_bgp_speaker(bgp_speaker) self.advertise_routes_via_bgp_speaker(bgp_speaker) self.schedule_resync(speaker_id=bgp_speaker['id'], reason="Periodic route cache refresh") def remove_bgp_speaker_from_dragent(self, bgp_speaker_id): if self.cache.is_bgp_speaker_added(bgp_speaker_id): bgp_speaker_as = self.cache.get_bgp_speaker_local_as( bgp_speaker_id) self.cache.remove_bgp_speaker_by_id(bgp_speaker_id) LOG.debug('Calling driver for removing BGP speaker %(speaker_as)s', {'speaker_as': bgp_speaker_as}) try: self.dr_driver_cls.delete_bgp_speaker(bgp_speaker_as) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'remove_bgp_speaker', e) return # Ideally, only the added speakers can be removed by the neutron # server. Looks like there might be some synchronization # issue between the server and the agent. Let's initiate a re-sync # to resolve the issue. self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") def add_bgp_peers_to_bgp_speaker(self, bgp_speaker): for bgp_peer in bgp_speaker['peers']: self.add_bgp_peer_to_bgp_speaker(bgp_speaker['id'], bgp_speaker['local_as'], bgp_peer) if self.is_resync_scheduled(bgp_speaker['id']): break def add_bgp_peer_to_bgp_speaker(self, bgp_speaker_id, bgp_speaker_as, bgp_peer): if self.cache.get_bgp_peer_by_ip(bgp_speaker_id, bgp_peer['peer_ip']): return self.cache.put_bgp_peer(bgp_speaker_id, bgp_peer) LOG.debug('Calling driver interface for adding BGP peer %(peer_ip)s ' 'remote_as=%(remote_as)s to BGP Speaker running for ' 'local_as=%(local_as)d ' 'auth_type=%(auth_type)s', {'peer_ip': bgp_peer['peer_ip'], 'remote_as': bgp_peer['remote_as'], 'local_as': bgp_speaker_as, 'auth_type': bgp_peer['auth_type']}) try: self.dr_driver_cls.add_bgp_peer(bgp_speaker_as, bgp_peer['peer_ip'], bgp_peer['remote_as'], bgp_peer['auth_type'], bgp_peer['password']) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'add_bgp_peer', e) def remove_bgp_peer_from_bgp_speaker(self, bgp_speaker_id, bgp_peer_ip): # Ideally BGP Speaker must be added by now, If not then let's # re-sync. if not self.cache.is_bgp_speaker_added(bgp_speaker_id): self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Speaker Out-of-sync") return if self.cache.is_bgp_peer_added(bgp_speaker_id, bgp_peer_ip): self.cache.remove_bgp_peer_by_ip(bgp_speaker_id, bgp_peer_ip) bgp_speaker_as = self.cache.get_bgp_speaker_local_as( bgp_speaker_id) LOG.debug('Calling driver interface to remove BGP peer ' '%(peer_ip)s from BGP Speaker running for ' 'local_as=%(local_as)d', {'peer_ip': bgp_peer_ip, 'local_as': bgp_speaker_as}) try: self.dr_driver_cls.delete_bgp_peer(bgp_speaker_as, bgp_peer_ip) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'remove_bgp_peer', e) return # Ideally, only the added peers can be removed by the neutron # server. Looks like there might be some synchronization # issue between the server and the agent. Let's initiate a re-sync # to resolve the issue. self.schedule_resync(speaker_id=bgp_speaker_id, reason="BGP Peer Out-of-sync") def advertise_routes_via_bgp_speaker(self, bgp_speaker): for route in bgp_speaker['advertised_routes']: self.advertise_route_via_bgp_speaker(bgp_speaker['id'], bgp_speaker['local_as'], route) if self.is_resync_scheduled(bgp_speaker['id']): break def advertise_route_via_bgp_speaker(self, bgp_speaker_id, bgp_speaker_as, route): if self.cache.is_route_advertised(bgp_speaker_id, route): # Requested route already advertised. Hence, Nothing to be done. return self.cache.put_adv_route(bgp_speaker_id, route) LOG.debug('Calling driver for advertising prefix: %(cidr)s, ' 'next_hop: %(nexthop)s', {'cidr': route['destination'], 'nexthop': route['next_hop']}) try: self.dr_driver_cls.advertise_route(bgp_speaker_as, route['destination'], route['next_hop']) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'advertise_route', e) def withdraw_route_via_bgp_speaker(self, bgp_speaker_id, bgp_speaker_as, route): if self.cache.is_route_advertised(bgp_speaker_id, route): self.cache.remove_adv_route(bgp_speaker_id, route) LOG.debug('Calling driver for withdrawing prefix: %(cidr)s, ' 'next_hop: %(nexthop)s', {'cidr': route['destination'], 'nexthop': route['next_hop']}) try: self.dr_driver_cls.withdraw_route(bgp_speaker_as, route['destination'], route['next_hop']) except Exception as e: self._handle_driver_failure(bgp_speaker_id, 'withdraw_route', e) return # Ideally, only the advertised routes can be withdrawn by the # neutron server. Looks like there might be some synchronization # issue between the server and the agent. Let's initiate a re-sync # to resolve the issue. self.schedule_resync(speaker_id=bgp_speaker_id, reason="Advertised routes Out-of-sync") def schedule_full_resync(self, reason): LOG.debug('Recording full resync request for all BGP Speakers ' 'with reason=%s', reason) self.needs_full_sync_reason = reason def schedule_resync(self, reason, speaker_id): """Schedule a full resync for a given BGP Speaker. If no BGP Speaker is specified, resync all BGP Speakers. """ LOG.debug('Recording resync request for BGP Speaker %s ' 'with reason=%s', speaker_id, reason) self.needs_resync_reasons[speaker_id].append(reason) def is_resync_scheduled(self, bgp_speaker_id): if bgp_speaker_id not in self.needs_resync_reasons: return False reason = self.needs_resync_reasons[bgp_speaker_id] # Re-sync scheduled for the queried BGP speaker. No point # continuing further. Let's stop processing and wait for # re-sync to happen. LOG.debug('Re-sync already scheduled for BGP Speaker %s ' 'with reason=%s', bgp_speaker_id, reason) return True class BgpDrPluginApi(object): """Agent side of BgpDrAgent RPC API. This class implements the client side of an rpc interface. The server side of this interface can be found in api.rpc.handlers.bgp_speaker_rpc.BgpSpeakerRpcCallback. For more information about changing rpc interfaces, see doc/source/devref/rpc_api.rst. API version history: 1.0 - Initial version. """ def __init__(self, topic, context, host): self.context = context self.host = host target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def get_bgp_speakers(self, context): """Make a remote process call to retrieve all BGP speakers info.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_bgp_speakers', host=self.host) def get_bgp_speaker_info(self, context, bgp_speaker_id): """Make a remote process call to retrieve a BGP speaker info.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_bgp_speaker_info', bgp_speaker_id=bgp_speaker_id) def get_bgp_peer_info(self, context, bgp_peer_id): """Make a remote process call to retrieve a BGP peer info.""" cctxt = self.client.prepare() return cctxt.call(context, 'get_bgp_peer_info', bgp_peer_id=bgp_peer_id) class BgpSpeakerCache(object): """Agent cache of the current BGP speaker state. This class is designed to support the advertisement for multiple BGP speaker via a single driver interface. Version history: 1.0 - Initial version for caching the state of BGP speaker. """ def __init__(self): self.cache = {} def get_bgp_speaker_ids(self): return self.cache.keys() def put_bgp_speaker(self, bgp_speaker): if bgp_speaker['id'] in self.cache: self.remove_bgp_speaker_by_id(self.cache[bgp_speaker['id']]) self.cache[bgp_speaker['id']] = {'bgp_speaker': bgp_speaker, 'peers': {}, 'advertised_routes': []} def get_bgp_speaker_by_id(self, bgp_speaker_id): if bgp_speaker_id in self.cache: return self.cache[bgp_speaker_id]['bgp_speaker'] def get_bgp_speaker_local_as(self, bgp_speaker_id): bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) if bgp_speaker: return bgp_speaker['local_as'] def is_bgp_speaker_added(self, bgp_speaker_id): return self.get_bgp_speaker_by_id(bgp_speaker_id) def remove_bgp_speaker_by_id(self, bgp_speaker_id): if bgp_speaker_id in self.cache: del self.cache[bgp_speaker_id] def put_bgp_peer(self, bgp_speaker_id, bgp_peer): if bgp_peer['peer_ip'] in self.get_bgp_peer_ips(bgp_speaker_id): del self.cache[bgp_speaker_id]['peers'][bgp_peer['peer_ip']] self.cache[bgp_speaker_id]['peers'][bgp_peer['peer_ip']] = bgp_peer def is_bgp_peer_added(self, bgp_speaker_id, bgp_peer_ip): return self.get_bgp_peer_by_ip(bgp_speaker_id, bgp_peer_ip) def get_bgp_peer_ips(self, bgp_speaker_id): bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) if bgp_speaker: return self.cache[bgp_speaker_id]['peers'].keys() def get_bgp_peer_by_ip(self, bgp_speaker_id, bgp_peer_ip): bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) if bgp_speaker: return self.cache[bgp_speaker_id]['peers'].get(bgp_peer_ip) def remove_bgp_peer_by_ip(self, bgp_speaker_id, bgp_peer_ip): if bgp_peer_ip in self.get_bgp_peer_ips(bgp_speaker_id): del self.cache[bgp_speaker_id]['peers'][bgp_peer_ip] def put_adv_route(self, bgp_speaker_id, route): self.cache[bgp_speaker_id]['advertised_routes'].append(route) def is_route_advertised(self, bgp_speaker_id, route): routes = self.cache[bgp_speaker_id]['advertised_routes'] for r in routes: if r['destination'] == route['destination'] and ( route['next_hop'] is None or r['next_hop'] == route['next_hop']): return True return False def remove_adv_route(self, bgp_speaker_id, route): routes = self.cache[bgp_speaker_id]['advertised_routes'] updated_routes = [r for r in routes if ( r['destination'] != route['destination'])] self.cache[bgp_speaker_id]['advertised_routes'] = updated_routes def get_adv_routes(self, bgp_speaker_id): return self.cache[bgp_speaker_id]['advertised_routes'] def get_state(self): bgp_speaker_ids = self.get_bgp_speaker_ids() num_bgp_speakers = len(bgp_speaker_ids) num_bgp_peers = 0 num_advertised_routes = 0 for bgp_speaker_id in bgp_speaker_ids: bgp_speaker = self.get_bgp_speaker_by_id(bgp_speaker_id) num_bgp_peers += len(bgp_speaker['peers']) num_advertised_routes += len(bgp_speaker['advertised_routes']) return {'bgp_speakers': num_bgp_speakers, 'bgp_peers': num_bgp_peers, 'advertise_routes': num_advertised_routes} class BgpDrAgentWithStateReport(BgpDrAgent): def __init__(self, host, conf=None): super(BgpDrAgentWithStateReport, self).__init__(host, conf) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.REPORTS) self.agent_state = { 'agent_type': bgp_consts.AGENT_TYPE_BGP_ROUTING, 'binary': 'neutron-bgp-dragent', 'configurations': {}, 'host': host, 'topic': bgp_consts.BGP_DRAGENT, 'start_flag': True} report_interval = cfg.CONF.AGENT.report_interval if report_interval: self.heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) self.heartbeat.start(interval=report_interval) def _report_state(self): LOG.debug("Report state task started") try: self.agent_state.get('configurations').update( self.cache.get_state()) ctx = context.get_admin_context_without_session() agent_status = self.state_rpc.report_state(ctx, self.agent_state, True) if agent_status == agent_consts.AGENT_REVIVED: LOG.info(_LI("Agent has just been revived. " "Scheduling full sync")) self.schedule_full_resync( reason=_("Agent has just been revived")) except AttributeError: # This means the server does not support report_state LOG.warning(_LW("Neutron server does not support state report. " "State report for this agent will be disabled.")) self.heartbeat.stop() self.run() return except Exception: LOG.exception(_LE("Failed reporting state!")) return if self.agent_state.pop('start_flag', None): self.run() def agent_updated(self, context, payload): """Handle the agent_updated notification event.""" self.schedule_full_resync( reason=_("BgpDrAgent updated: %s") % payload) LOG.info(_LI("agent_updated by server side %s!"), payload) def after_start(self): LOG.info(_LI("BGP dynamic routing agent started")) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/config.py0000664000175000017500000000176300000000000030701 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from oslo_config import cfg from neutron_dynamic_routing._i18n import _ BGP_DRIVER_OPTS = [ cfg.StrOpt('bgp_speaker_driver', help=_("BGP speaker driver class to be instantiated.")) ] BGP_PROTO_CONFIG_OPTS = [ cfg.StrOpt('bgp_router_id', help=_("32-bit BGP identifier, typically an IPv4 address " "owned by the system running the BGP DrAgent.")) ] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/0000775000175000017500000000000000000000000030346 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/__init__.py0000664000175000017500000000000000000000000032445 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/base.py0000664000175000017500000001371500000000000031641 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import abc class BgpDriverBase(object, metaclass=abc.ABCMeta): """Base class for BGP Speaking drivers. Any class which provides BGP functionality should extend this defined base class. """ @abc.abstractmethod def add_bgp_speaker(self, speaker_as): """Add a BGP speaker. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :raises: BgpSpeakerAlreadyScheduled, BgpSpeakerMaxScheduled, InvalidParamType, InvalidParamRange """ @abc.abstractmethod def delete_bgp_speaker(self, speaker_as): """Deletes BGP speaker. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :raises: BgpSpeakerNotAdded """ @abc.abstractmethod def add_bgp_peer(self, speaker_as, peer_ip, peer_as, auth_type='none', password=None): """Add a new BGP peer. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param peer_ip: Specifies the IP address of the peer. :type peer_ip: string :param peer_as: Specifies Autonomous Number of the peer. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type peer_as: biginteger :param auth_type: Specifies authentication type. By default, authentication will be disabled. :type auth_type: value in SUPPORTED_AUTH_TYPES :param password: Authentication password.By default, authentication will be disabled. :type password: string :raises: BgpSpeakerNotAdded, InvalidParamType, InvalidParamRange, InvaildAuthType, PasswordNotSpecified """ @abc.abstractmethod def delete_bgp_peer(self, speaker_as, peer_ip): """Delete a BGP peer associated with the given peer IP :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param peer_ip: Specifies the IP address of the peer. Must be the string representation of an IP address. :type peer_ip: string :raises: BgpSpeakerNotAdded, BgpPeerNotAdded """ @abc.abstractmethod def advertise_route(self, speaker_as, cidr, nexthop): """Add a new prefix to advertise. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param cidr: CIDR of the network to advertise. Must be the string representation of an IP network (e.g., 10.1.1.0/24) :type cidr: string :param nexthop: Specifies the next hop address for the above prefix. :type nexthop: string :raises: BgpSpeakerNotAdded, InvalidParamType """ @abc.abstractmethod def withdraw_route(self, speaker_as, cidr, nexthop=None): """Withdraw an advertised prefix. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param cidr: CIDR of the network to withdraw. Must be the string representation of an IP network (e.g., 10.1.1.0/24) :type cidr: string :param nexthop: Specifies the next hop address for the above prefix. :type nexthop: string :raises: BgpSpeakerNotAdded, RouteNotAdvertised, InvalidParamType """ @abc.abstractmethod def get_bgp_speaker_statistics(self, speaker_as): """Collect BGP Speaker statistics. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :raises: BgpSpeakerNotAdded :returns: bgp_speaker_stats: string """ @abc.abstractmethod def get_bgp_peer_statistics(self, speaker_as, peer_ip, peer_as): """Collect BGP Peer statistics. :param speaker_as: Specifies BGP Speaker autonomous system number. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type speaker_as: biginteger :param peer_ip: Specifies the IP address of the peer. :type peer_ip: string :param peer_as: Specifies the AS number of the peer. Must be an biginteger between MIN_ASNUM and MAX_4BYTE_ASNUM. :type peer_as: biginteger . :raises: BgpSpeakerNotAdded, BgpPeerNotAdded :returns: bgp_peer_stats: string """ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/exceptions.py0000664000175000017500000000402600000000000033103 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import exceptions as n_exc from neutron_dynamic_routing._i18n import _ # BGP Driver Exceptions class BgpSpeakerNotAdded(n_exc.BadRequest): message = _("BGP Speaker for local_as=%(local_as)s with " "router_id=%(rtid)s not added yet.") class BgpSpeakerMaxScheduled(n_exc.BadRequest): message = _("Already hosting maximum number of BGP Speakers. " "Allowed scheduled count=%(count)d") class BgpSpeakerAlreadyScheduled(n_exc.Conflict): message = _("Already hosting BGP Speaker for local_as=%(current_as)d with " "router_id=%(rtid)s.") class BgpPeerNotAdded(n_exc.BadRequest): message = _("BGP Peer %(peer_ip)s for remote_as=%(remote_as)s, running " "for BGP Speaker %(speaker_as)d not added yet.") class RouteNotAdvertised(n_exc.BadRequest): message = _("Route %(cidr)s not advertised for BGP Speaker " "%(speaker_as)d.") class InvalidParamType(n_exc.NeutronException): message = _("Parameter %(param)s must be of %(param_type)s type.") class InvalidParamRange(n_exc.NeutronException): message = _("%(param)s must be in %(range)s range.") class InvaildAuthType(n_exc.BadRequest): message = _("Authentication type not supported. Requested " "type=%(auth_type)s.") class PasswordNotSpecified(n_exc.BadRequest): message = _("Password not specified for authentication " "type=%(auth_type)s.") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3966606 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/os_ken/0000775000175000017500000000000000000000000031624 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/os_ken/__init__.py0000664000175000017500000000000000000000000033723 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/os_ken/driver.py0000664000175000017500000002266500000000000033504 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from os_ken.services.protocols.bgp import bgpspeaker from os_ken.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_ACTIVE from oslo_log import log as logging from oslo_utils import encodeutils from neutron_dynamic_routing._i18n import _LE, _LI from neutron_dynamic_routing.services.bgp.agent.driver import base from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.agent.driver import utils LOG = logging.getLogger(__name__) # Function for logging BGP peer and path changes. def bgp_peer_down_cb(remote_ip, remote_as): LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d went DOWN.'), {'peer_ip': remote_ip, 'peer_as': remote_as}) def bgp_peer_up_cb(remote_ip, remote_as): LOG.info(_LI('BGP Peer %(peer_ip)s for remote_as=%(peer_as)d is UP.'), {'peer_ip': remote_ip, 'peer_as': remote_as}) def best_path_change_cb(event): LOG.info(_LI("Best path change observed. cidr=%(prefix)s, " "nexthop=%(nexthop)s, remote_as=%(remote_as)d, " "is_withdraw=%(is_withdraw)s"), {'prefix': event.prefix, 'nexthop': event.nexthop, 'remote_as': event.remote_as, 'is_withdraw': event.is_withdraw}) class OsKenBgpDriver(base.BgpDriverBase): """BGP speaker implementation via os-ken.""" def __init__(self, cfg): LOG.info(_LI('Initializing os-ken driver for BGP functionality.')) self._read_config(cfg) # Note: Even though os-ken can only support one BGP speaker as of now, # we have tried making the framework generic for the future purposes. self.cache = utils.BgpMultiSpeakerCache() def _read_config(self, cfg): if cfg is None or cfg.bgp_router_id is None: # If either cfg or router_id is not specified, raise voice LOG.error(_LE('BGP router-id MUST be specified for the correct ' 'functional working.')) else: self.routerid = cfg.bgp_router_id LOG.info(_LI('Initialized os-ken BGP Speaker driver interface ' 'with bgp_router_id=%s'), self.routerid) def add_bgp_speaker(self, speaker_as): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if curr_speaker is not None: raise bgp_driver_exc.BgpSpeakerAlreadyScheduled( current_as=speaker_as, rtid=self.routerid) # os-ken can only support One speaker if self.cache.get_hosted_bgp_speakers_count() == 1: raise bgp_driver_exc.BgpSpeakerMaxScheduled(count=1) # Validate input parameters. # speaker_as must be an integer in the allowed range. utils.validate_as_num('local_as', speaker_as) # Notify os-ken about BGP Speaker addition. # Please note: Since, only the route-advertisement support is # implemented we are explicitly setting the bgp_server_port # attribute to 0 which disables listening on port 179. curr_speaker = bgpspeaker.BGPSpeaker(as_number=speaker_as, router_id=self.routerid, bgp_server_port=0, best_path_change_handler=best_path_change_cb, peer_down_handler=bgp_peer_down_cb, peer_up_handler=bgp_peer_up_cb) LOG.info(_LI('Added BGP Speaker for local_as=%(as)d with ' 'router_id= %(rtid)s.'), {'as': speaker_as, 'rtid': self.routerid}) self.cache.put_bgp_speaker(speaker_as, curr_speaker) def delete_bgp_speaker(self, speaker_as): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Notify os-ken about BGP Speaker deletion curr_speaker.shutdown() LOG.info(_LI('Removed BGP Speaker for local_as=%(as)d with ' 'router_id=%(rtid)s.'), {'as': speaker_as, 'rtid': self.routerid}) self.cache.remove_bgp_speaker(speaker_as) def add_bgp_peer(self, speaker_as, peer_ip, peer_as, auth_type='none', password=None): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate peer_ip and peer_as. utils.validate_as_num('remote_as', peer_as) utils.validate_ip_addr(peer_ip) utils.validate_auth(auth_type, password) if password is not None: password = encodeutils.to_utf8(password) curr_speaker.neighbor_add(address=peer_ip, remote_as=peer_as, enable_ipv4=True, enable_ipv6=True, password=password, connect_mode=CONNECT_MODE_ACTIVE) LOG.info(_LI('Added BGP Peer %(peer)s for remote_as=%(as)d to ' 'BGP Speaker running for local_as=%(local_as)d.'), {'peer': peer_ip, 'as': peer_as, 'local_as': speaker_as}) def delete_bgp_peer(self, speaker_as, peer_ip): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate peer_ip. It must be a string. utils.validate_ip_addr(peer_ip) # Notify os-ken about BGP Peer removal curr_speaker.neighbor_del(address=peer_ip) LOG.info(_LI('Removed BGP Peer %(peer)s from BGP Speaker ' 'running for local_as=%(local_as)d.'), {'peer': peer_ip, 'local_as': speaker_as}) def advertise_route(self, speaker_as, cidr, nexthop): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate cidr and nexthop. Both must be strings. utils.validate_string(cidr) utils.validate_string(nexthop) # Notify os-ken about route advertisement curr_speaker.prefix_add(prefix=cidr, next_hop=nexthop) LOG.info(_LI('Route cidr=%(prefix)s, nexthop=%(nexthop)s is ' 'advertised for BGP Speaker running for ' 'local_as=%(local_as)d.'), {'prefix': cidr, 'nexthop': nexthop, 'local_as': speaker_as}) def withdraw_route(self, speaker_as, cidr, nexthop=None): curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # Validate cidr. It must be a string. utils.validate_string(cidr) # Notify os-ken about route withdrawal curr_speaker.prefix_del(prefix=cidr) LOG.info(_LI('Route cidr=%(prefix)s is withdrawn from BGP Speaker ' 'running for local_as=%(local_as)d.'), {'prefix': cidr, 'local_as': speaker_as}) def get_bgp_speaker_statistics(self, speaker_as): LOG.info(_LI('Collecting BGP Speaker statistics for local_as=%d.'), speaker_as) curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # TODO(vikram): Filter and return the necessary information. # Will be done as part of new RFE requirement # https://bugs.launchpad.net/neutron/+bug/1527993 return curr_speaker.neighbor_state_get() def get_bgp_peer_statistics(self, speaker_as, peer_ip): LOG.info(_LI('Collecting BGP Peer statistics for peer_ip=%(peer)s, ' 'running in speaker_as=%(speaker_as)d '), {'peer': peer_ip, 'speaker_as': speaker_as}) curr_speaker = self.cache.get_bgp_speaker(speaker_as) if not curr_speaker: raise bgp_driver_exc.BgpSpeakerNotAdded(local_as=speaker_as, rtid=self.routerid) # TODO(vikram): Filter and return the necessary information. # Will be done as part of new RFE requirement # https://bugs.launchpad.net/neutron/+bug/1527993 return curr_speaker.neighbor_state_get(address=peer_ip) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/driver/utils.py0000664000175000017500000000612700000000000032066 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import netaddr from neutron_lib import constants as lib_consts from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa # Parameter validation functions provided are provided by the base. def validate_as_num(param, as_num): if not isinstance(as_num, int): raise bgp_driver_exc.InvalidParamType(param=param, param_type='integer') if not (bgp_consts.MIN_ASNUM <= as_num <= bgp_consts.MAX_4BYTE_ASNUM): # Must be in [AS_NUM_MIN, MAX_4BYTE_ASNUM] range. allowed_range = ('[' + str(bgp_consts.MIN_ASNUM) + '-' + str(bgp_consts.MAX_4BYTE_ASNUM) + ']') raise bgp_driver_exc.InvalidParamRange(param=param, range=allowed_range) def validate_auth(auth_type, password): validate_string(password) if auth_type in bgp_consts.SUPPORTED_AUTH_TYPES: if auth_type != 'none' and password is None: raise bgp_driver_exc.PasswordNotSpecified(auth_type=auth_type) if auth_type == 'none' and password is not None: raise bgp_driver_exc.InvaildAuthType(auth_type=auth_type) else: raise bgp_driver_exc.InvaildAuthType(auth_type=auth_type) def validate_ip_addr(ip_addr): if netaddr.valid_ipv4(ip_addr): return lib_consts.IP_VERSION_4 elif netaddr.valid_ipv6(ip_addr): return lib_consts.IP_VERSION_6 else: raise bgp_driver_exc.InvalidParamType(param=ip_addr, param_type='ip-address') def validate_string(param): if param is not None: if not isinstance(param, str): raise bgp_driver_exc.InvalidParamType(param=param, param_type='string') class BgpMultiSpeakerCache(object): """Class for saving multiple BGP speakers information. Version history: 1.0 - Initial version for caching multiple BGP speaker information. """ def __init__(self): self.cache = {} def get_hosted_bgp_speakers_count(self): return len(self.cache) def put_bgp_speaker(self, local_as, speaker): self.cache[local_as] = speaker def get_bgp_speaker(self, local_as): return self.cache.get(local_as) def remove_bgp_speaker(self, local_as): self.cache.pop(local_as, None) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/agent/entry.py0000664000175000017500000000337400000000000030575 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import sys from oslo_config import cfg from oslo_service import service from neutron.common import config as common_config from neutron.conf.agent import common as config from neutron import service as neutron_service from neutron_dynamic_routing.services.bgp.agent import config as bgp_dragent_config # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa def register_options(): config.register_agent_state_opts_helper(cfg.CONF) config.register_root_helper(cfg.CONF) cfg.CONF.register_opts(bgp_dragent_config.BGP_DRIVER_OPTS, 'BGP') cfg.CONF.register_opts(bgp_dragent_config.BGP_PROTO_CONFIG_OPTS, 'BGP') config.register_external_process_opts(cfg.CONF) def main(): register_options() common_config.init(sys.argv[1:]) config.setup_logging() server = neutron_service.Service.create( binary='neutron-bgp-dragent', topic=bgp_consts.BGP_DRAGENT, report_interval=cfg.CONF.AGENT.report_interval, manager='neutron_dynamic_routing.services.bgp.agent.bgp_dragent.' 'BgpDrAgentWithStateReport') service.launch(cfg.CONF, server, restart_method='mutate').wait() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/bgp_plugin.py0000664000175000017500000005107400000000000030464 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from netaddr import IPAddress from neutron_lib.api.definitions import bgp as bgp_ext from neutron_lib.api.definitions import bgp_4byte_asn from neutron_lib.api.definitions import bgp_dragentscheduler as dras_ext from neutron_lib.api.definitions import portbindings from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_lib import context from neutron_lib import rpc as n_rpc from neutron_lib.services import base as service_base from oslo_config import cfg from oslo_log import log as logging from oslo_utils import importutils from neutron_dynamic_routing.api.rpc.agentnotifiers import bgp_dr_rpc_agent_api # noqa from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.api.rpc.handlers import bgp_speaker_rpc as bs_rpc from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts PLUGIN_NAME = bgp_ext.ALIAS + '_svc_plugin' LOG = logging.getLogger(__name__) class BgpPlugin(service_base.ServicePluginBase, bgp_db.BgpDbMixin, bgp_dragentscheduler_db.BgpDrAgentSchedulerDbMixin): supported_extension_aliases = [bgp_ext.ALIAS, dras_ext.ALIAS, bgp_4byte_asn.ALIAS] def __init__(self): super(BgpPlugin, self).__init__() self.bgp_drscheduler = importutils.import_object( cfg.CONF.bgp_drscheduler_driver) self._setup_rpc() self._register_callbacks() self.add_periodic_dragent_status_check() def get_plugin_type(self): return bgp_ext.ALIAS def get_plugin_description(self): """returns string description of the plugin.""" return ("BGP dynamic routing service for announcement of next-hops " "for project networks, floating IP's, and DVR host routes.") def _setup_rpc(self): self.topic = bgp_consts.BGP_PLUGIN self.conn = n_rpc.Connection() self.agent_notifiers[bgp_consts.AGENT_TYPE_BGP_ROUTING] = ( bgp_dr_rpc_agent_api.BgpDrAgentNotifyApi() ) self._bgp_rpc = self.agent_notifiers[bgp_consts.AGENT_TYPE_BGP_ROUTING] self.endpoints = [bs_rpc.BgpSpeakerRpcCallback()] self.conn.create_consumer(self.topic, self.endpoints, fanout=False) self.conn.consume_in_threads() def _register_callbacks(self): registry.subscribe(self.floatingip_update_callback, resources.FLOATING_IP, events.AFTER_CREATE) registry.subscribe(self.floatingip_update_callback, resources.FLOATING_IP, events.AFTER_UPDATE) registry.subscribe(self.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_CREATE) registry.subscribe(self.router_interface_callback, resources.ROUTER_INTERFACE, events.BEFORE_CREATE) registry.subscribe(self.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_DELETE) registry.subscribe(self.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_CREATE) registry.subscribe(self.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_DELETE) registry.subscribe(self.port_callback, resources.PORT, events.AFTER_UPDATE) def get_bgp_speakers(self, context, filters=None, fields=None, sorts=None, limit=None, marker=None, page_reverse=False): return super(BgpPlugin, self).get_bgp_speakers( context, filters=filters, fields=fields, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) def get_bgp_speaker(self, context, bgp_speaker_id, fields=None): return super(BgpPlugin, self).get_bgp_speaker(context, bgp_speaker_id, fields=fields) def create_bgp_speaker(self, context, bgp_speaker): bgp_speaker = super(BgpPlugin, self).create_bgp_speaker(context, bgp_speaker) registry.publish(dr_resources.BGP_SPEAKER, events.AFTER_CREATE, self, payload=events.DBEventPayload( context, metadata={'plugin': self}, states=(bgp_speaker,))) return bgp_speaker def update_bgp_speaker(self, context, bgp_speaker_id, bgp_speaker): return super(BgpPlugin, self).update_bgp_speaker(context, bgp_speaker_id, bgp_speaker) def delete_bgp_speaker(self, context, bgp_speaker_id): hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) super(BgpPlugin, self).delete_bgp_speaker(context, bgp_speaker_id) for agent in hosted_bgp_dragents: self._bgp_rpc.bgp_speaker_removed(context, bgp_speaker_id, agent.host) def get_bgp_peers(self, context, fields=None, filters=None, sorts=None, limit=None, marker=None, page_reverse=False): return super(BgpPlugin, self).get_bgp_peers( context, fields=fields, filters=filters, sorts=sorts, limit=limit, marker=marker, page_reverse=page_reverse) def get_bgp_peer(self, context, bgp_peer_id, fields=None): return super(BgpPlugin, self).get_bgp_peer(context, bgp_peer_id, fields=fields) def create_bgp_peer(self, context, bgp_peer): return super(BgpPlugin, self).create_bgp_peer(context, bgp_peer) def update_bgp_peer(self, context, bgp_peer_id, bgp_peer): return super(BgpPlugin, self).update_bgp_peer(context, bgp_peer_id, bgp_peer) def delete_bgp_peer(self, context, bgp_peer_id): super(BgpPlugin, self).delete_bgp_peer(context, bgp_peer_id) def add_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): ret_value = super(BgpPlugin, self).add_bgp_peer(context, bgp_speaker_id, bgp_peer_info) hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) for agent in hosted_bgp_dragents: self._bgp_rpc.bgp_peer_associated(context, bgp_speaker_id, ret_value['bgp_peer_id'], agent.host) return ret_value def remove_bgp_peer(self, context, bgp_speaker_id, bgp_peer_info): hosted_bgp_dragents = self.get_dragents_hosting_bgp_speakers( context, [bgp_speaker_id]) ret_value = super(BgpPlugin, self).remove_bgp_peer(context, bgp_speaker_id, bgp_peer_info) for agent in hosted_bgp_dragents: self._bgp_rpc.bgp_peer_disassociated(context, bgp_speaker_id, ret_value['bgp_peer_id'], agent.host) def add_bgp_speaker_to_dragent(self, context, agent_id, speaker_id): super(BgpPlugin, self).add_bgp_speaker_to_dragent(context, agent_id, speaker_id) def remove_bgp_speaker_from_dragent(self, context, agent_id, speaker_id): super(BgpPlugin, self).remove_bgp_speaker_from_dragent(context, agent_id, speaker_id) def list_bgp_speaker_on_dragent(self, context, agent_id): return super(BgpPlugin, self).list_bgp_speaker_on_dragent(context, agent_id) def list_dragent_hosting_bgp_speaker(self, context, speaker_id): return super(BgpPlugin, self).list_dragent_hosting_bgp_speaker( context, speaker_id) def add_gateway_network(self, context, bgp_speaker_id, network_info): return super(BgpPlugin, self).add_gateway_network(context, bgp_speaker_id, network_info) def remove_gateway_network(self, context, bgp_speaker_id, network_info): return super(BgpPlugin, self).remove_gateway_network(context, bgp_speaker_id, network_info) def get_advertised_routes(self, context, bgp_speaker_id): return super(BgpPlugin, self).get_advertised_routes(context, bgp_speaker_id) def floatingip_update_callback(self, resource, event, trigger, payload): if event not in [events.AFTER_CREATE, events.AFTER_UPDATE]: return ctx = context.get_admin_context() new_fip = payload.latest_state new_router_id = new_fip['router_id'] floating_ip_address = new_fip['floating_ip_address'] dest = str(floating_ip_address) + '/32' bgp_speakers = self._bgp_speakers_for_gw_network_by_family( ctx, new_fip['floating_network_id'], n_const.IP_VERSION_4) last_router_id = None if event == events.AFTER_UPDATE: old_fip = payload.states[0] last_router_id = old_fip['router_id'] if last_router_id and new_router_id != last_router_id: # Here gives the old route next_hop a `None` value, then # the DR agent side will withdraw it. old_host_route = {'destination': dest, 'next_hop': None} for bgp_speaker in bgp_speakers: self.stop_route_advertisements(ctx, self._bgp_rpc, bgp_speaker.id, [old_host_route]) if new_router_id and new_router_id != last_router_id: next_hop = self._get_fip_next_hop( ctx, new_router_id, floating_ip_address) new_host_route = {'destination': dest, 'next_hop': next_hop} for bgp_speaker in bgp_speakers: self.start_route_advertisements(ctx, self._bgp_rpc, bgp_speaker.id, [new_host_route]) def router_interface_callback(self, resource, event, trigger, payload=None): if event == events.AFTER_CREATE: self._handle_router_interface_after_create(payload) if event == events.AFTER_DELETE: gw_network = payload.metadata.get('network_id') next_hops = self._next_hops_from_gateway_ips( payload.metadata.get('gateway_ips')) ctx = context.get_admin_context() speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) for speaker in speakers: routes = self._route_list_from_prefixes_and_next_hop( payload.metadata['cidrs'], next_hops[speaker.ip_version]) self._handle_router_interface_after_delete(gw_network, routes) def _handle_router_interface_after_create(self, payload): gw_network = payload.metadata.get('network_id') if not gw_network: return ctx = context.get_admin_context() with ctx.session.begin(subtransactions=True): speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) next_hops = self._next_hops_from_gateway_ips( payload.metadata.get('gateway_ips')) for speaker in speakers: prefixes = self._tenant_prefixes_by_router( ctx, payload.resource_id, speaker.id) next_hop = next_hops.get(speaker.ip_version) if next_hop: rl = self._route_list_from_prefixes_and_next_hop(prefixes, next_hop) self.start_route_advertisements(ctx, self._bgp_rpc, speaker.id, rl) def router_gateway_callback(self, resource, event, trigger, payload=None): if event == events.AFTER_CREATE: self._handle_router_gateway_after_create(payload) if event == events.AFTER_DELETE: gw_network = payload.metadata.get('network_id') router_id = payload.resource_id next_hops = self._next_hops_from_gateway_ips( payload.metadata.get('gateway_ips')) ctx = context.get_admin_context() speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) for speaker in speakers: if speaker.ip_version in next_hops: next_hop = next_hops[speaker.ip_version] prefixes = self._tenant_prefixes_by_router(ctx, router_id, speaker.id) routes = self._route_list_from_prefixes_and_next_hop( prefixes, next_hop) self._handle_router_interface_after_delete(gw_network, routes) def _handle_router_gateway_after_create(self, payload): ctx = context.get_admin_context() gw_network = payload.metadata.get('network_id') router_id = payload.resource_id with ctx.session.begin(subtransactions=True): speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) next_hops = self._next_hops_from_gateway_ips( payload.metadata.get('gateway_ips')) for speaker in speakers: if speaker.ip_version in next_hops: next_hop = next_hops[speaker.ip_version] prefixes = self._tenant_prefixes_by_router(ctx, router_id, speaker.id) routes = self._route_list_from_prefixes_and_next_hop( prefixes, next_hop) self.start_route_advertisements(ctx, self._bgp_rpc, speaker.id, routes) def _handle_router_interface_after_delete(self, gw_network, routes): if gw_network and routes: ctx = context.get_admin_context() speakers = self._bgp_speakers_for_gateway_network(ctx, gw_network) for speaker in speakers: self.stop_route_advertisements(ctx, self._bgp_rpc, speaker.id, routes) def port_callback(self, resource, event, trigger, payload): if event != events.AFTER_UPDATE: return original_port = payload.states[0] updated_port = payload.latest_state if not updated_port.get('fixed_ips'): return original_host = original_port.get(portbindings.HOST_ID) updated_host = updated_port.get(portbindings.HOST_ID) device_owner = updated_port.get('device_owner') # if host in the port binding has changed, update next-hops if original_host != updated_host and bool('compute:' in device_owner): ctx = context.get_admin_context() with ctx.session.begin(subtransactions=True): ext_nets = self.get_external_networks_for_port(ctx, updated_port) for ext_net in ext_nets: bgp_speakers = ( self._get_bgp_speaker_ids_by_binding_network( ctx, ext_nets)) # Refresh any affected BGP speakers for bgp_speaker in bgp_speakers: routes = self.get_advertised_routes(ctx, bgp_speaker) self.start_route_advertisements(ctx, self._bgp_rpc, bgp_speaker, routes) def _next_hops_from_gateway_ips(self, gw_ips): if gw_ips: return {IPAddress(ip).version: ip for ip in gw_ips} return {} def start_route_advertisements(self, ctx, bgp_rpc, bgp_speaker_id, routes): agents = self.list_dragent_hosting_bgp_speaker(ctx, bgp_speaker_id) for agent in agents['agents']: bgp_rpc.bgp_routes_advertisement(ctx, bgp_speaker_id, routes, agent['host']) msg = "Starting route advertisements for %s on BgpSpeaker %s" self._debug_log_for_routes(msg, routes, bgp_speaker_id) def stop_route_advertisements(self, ctx, bgp_rpc, bgp_speaker_id, routes): agents = self.list_dragent_hosting_bgp_speaker(ctx, bgp_speaker_id) for agent in agents['agents']: bgp_rpc.bgp_routes_withdrawal(ctx, bgp_speaker_id, routes, agent['host']) msg = "Stopping route advertisements for %s on BgpSpeaker %s" self._debug_log_for_routes(msg, routes, bgp_speaker_id) def _debug_log_for_routes(self, msg, routes, bgp_speaker_id): # Could have a large number of routes passed, check log level first if LOG.isEnabledFor(logging.DEBUG): for route in routes: LOG.debug(msg, route, bgp_speaker_id) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/common/0000775000175000017500000000000000000000000027245 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/common/__init__.py0000664000175000017500000000000000000000000031344 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/common/constants.py0000664000175000017500000000161200000000000031633 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. AGENT_TYPE_BGP_ROUTING = 'BGP dynamic routing agent' BGP_DRAGENT = 'bgp_dragent' BGP_PLUGIN = 'q-bgp-plugin' # List of supported authentication types. SUPPORTED_AUTH_TYPES = ['none', 'md5'] # Supported AS number range MIN_ASNUM = 1 MAX_ASNUM = 65535 MAX_4BYTE_ASNUM = 4294967295 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/common/opts.py0000664000175000017500000000174100000000000030607 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import neutron_dynamic_routing.services.bgp.agent.config def list_bgp_agent_opts(): return [ ('bgp', itertools.chain( neutron_dynamic_routing.services.bgp.agent. config.BGP_DRIVER_OPTS, neutron_dynamic_routing.services.bgp.agent. config.BGP_PROTO_CONFIG_OPTS) ) ] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/scheduler/0000775000175000017500000000000000000000000027733 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/scheduler/__init__.py0000664000175000017500000000000000000000000032032 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/scheduler/bgp_dragent_scheduler.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/services/bgp/scheduler/bgp_dragent_scheduler.0000664000175000017500000002213600000000000034252 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context as nl_context from neutron_lib.db import api as db_api from neutron_lib.objects import registry as obj_reg from oslo_db import exception as db_exc from oslo_log import log as logging from sqlalchemy import sql from neutron.agent.common import utils from neutron.db.models import agent as agent_model from neutron.scheduler import base_resource_filter from neutron.scheduler import base_scheduler from neutron_dynamic_routing._i18n import _LI, _LW from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts LOG = logging.getLogger(__name__) BGP_SPEAKER_PER_DRAGENT = 1 class BgpDrAgentFilter(base_resource_filter.BaseResourceFilter): def bind(self, context, agents, bgp_speaker_id, force_scheduling=False): """Bind the BgpSpeaker to a BgpDrAgent.""" bound_agents = agents[:] for agent in agents: # saving agent_id to use it after rollback to avoid # DetachedInstanceError agent_id = agent.id binding = bgp_dras_db.BgpSpeakerDrAgentBinding() binding.agent_id = agent_id binding.bgp_speaker_id = bgp_speaker_id try: with db_api.CONTEXT_WRITER.using(context): context.session.add(binding) except db_exc.DBDuplicateEntry: # it's totally ok, someone just did our job! bound_agents.remove(agent) LOG.info(_LI('BgpDrAgent %s already present'), agent_id) LOG.debug('BgpSpeaker %(bgp_speaker_id)s is scheduled to be ' 'hosted by BgpDrAgent %(agent_id)s', {'bgp_speaker_id': bgp_speaker_id, 'agent_id': agent_id}) super(BgpDrAgentFilter, self).bind(context, bound_agents, bgp_speaker_id, force_scheduling) def filter_agents(self, plugin, context, bgp_speaker): """Return the agents that can host the BgpSpeaker.""" agents_dict = self._get_bgp_speaker_hostable_dragents( plugin, context, bgp_speaker) if not agents_dict['hostable_agents'] or agents_dict['n_agents'] <= 0: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} return agents_dict def _get_active_dragents(self, plugin, context): """Return a list of active BgpDrAgents.""" with db_api.CONTEXT_READER.using(context): active_dragents = plugin.get_agent_objects( context, filters={ 'agent_type': [bgp_consts.AGENT_TYPE_BGP_ROUTING], 'admin_state_up': [True]}) if not active_dragents: return [] return active_dragents def _get_num_dragents_hosting_bgp_speaker(self, bgp_speaker_id, dragent_bindings): return sum(1 if dragent_binding.bgp_speaker_id == bgp_speaker_id else 0 for dragent_binding in dragent_bindings) def _get_bgp_speaker_hostable_dragents(self, plugin, context, bgp_speaker): """Return number of additional BgpDrAgents which will actually host the given BgpSpeaker and a list of BgpDrAgents which can host the given BgpSpeaker """ # only one BgpSpeaker can be hosted by a BgpDrAgent for now. dragents_per_bgp_speaker = BGP_SPEAKER_PER_DRAGENT dragent_bindings = plugin.get_dragent_bgp_speaker_bindings(context) agents_hosting = [dragent_binding.agent_id for dragent_binding in dragent_bindings] num_dragents_hosting_bgp_speaker = ( self._get_num_dragents_hosting_bgp_speaker(bgp_speaker['id'], dragent_bindings)) n_agents = dragents_per_bgp_speaker - num_dragents_hosting_bgp_speaker if n_agents <= 0: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} active_dragents = self._get_active_dragents(plugin, context) hostable_dragents = [ agent for agent in set(active_dragents) if agent.id not in agents_hosting and plugin.is_eligible_agent( active=True, agent=agent) ] if not hostable_dragents: return {'n_agents': 0, 'hostable_agents': [], 'hosted_agents': []} n_agents = min(len(hostable_dragents), n_agents) return {'n_agents': n_agents, 'hostable_agents': hostable_dragents, 'hosted_agents': num_dragents_hosting_bgp_speaker} class BgpDrAgentSchedulerBase(BgpDrAgentFilter): def _register_callbacks(self): registry.subscribe(self.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE) def schedule_all_unscheduled_bgp_speakers(self, context): """Call schedule_unscheduled_bgp_speakers for all hosts. """ with db_api.CONTEXT_WRITER.using(context): query = context.session.query(agent_model.Agent.host).distinct() for agent in query: self.schedule_unscheduled_bgp_speakers(context, agent[0]) return True def schedule_unscheduled_bgp_speakers(self, context, host): """Schedule unscheduled BgpSpeaker to a BgpDrAgent. """ LOG.debug('Started auto-scheduling on host %s', host) with db_api.CONTEXT_WRITER.using(context): bgp_dragent = obj_reg.load_class('Agent').get_object( context, agent_type=bgp_consts.AGENT_TYPE_BGP_ROUTING, host=host, admin_state_up=True) if not bgp_dragent: LOG.debug('No enabled BgpDrAgent on host %s', host) return False if utils.is_agent_down( bgp_dragent.heartbeat_timestamp): LOG.warning(_LW('BgpDrAgent %s is down'), bgp_dragent.id) return False if self._is_bgp_speaker_hosted(context, bgp_dragent['id']): # One BgpDrAgent can only host one BGP speaker LOG.debug('BgpDrAgent already hosting a speaker on host %s. ' 'Cannot schedule an another one', host) return False unscheduled_speakers = self._get_unscheduled_bgp_speakers(context) if not unscheduled_speakers: LOG.debug('Nothing to auto-schedule on host %s', host) return False self.bind(context, [bgp_dragent], unscheduled_speakers[0]) return True def _is_bgp_speaker_hosted(self, context, agent_id): speaker_binding_model = bgp_dras_db.BgpSpeakerDrAgentBinding query = context.session.query(speaker_binding_model) query = query.filter(speaker_binding_model.agent_id == agent_id) return query.count() > 0 def _get_unscheduled_bgp_speakers(self, context): """BGP speakers that needs to be scheduled. """ no_agent_binding = ~sql.exists().where( bgp_db.BgpSpeaker.id == bgp_dras_db.BgpSpeakerDrAgentBinding.bgp_speaker_id) query = context.session.query(bgp_db.BgpSpeaker.id).filter( no_agent_binding) return [bgp_speaker_id_[0] for bgp_speaker_id_ in query] def schedule_bgp_speaker_callback(self, resource, event, trigger, payload): plugin = payload.metadata['plugin'] bgp_speaker = payload.latest_state if event == events.AFTER_CREATE: ctx = nl_context.get_admin_context() plugin.schedule_bgp_speaker(ctx, bgp_speaker) class ChanceScheduler(base_scheduler.BaseChanceScheduler, BgpDrAgentSchedulerBase): def __init__(self): super(ChanceScheduler, self).__init__(self) self._register_callbacks() class WeightScheduler(base_scheduler.BaseWeightScheduler, BgpDrAgentSchedulerBase): def __init__(self): super(WeightScheduler, self).__init__(self) self._register_callbacks() class StaticScheduler(BgpDrAgentFilter): def __init__(self): super(StaticScheduler, self).__init__() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/0000775000175000017500000000000000000000000024524 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/__init__.py0000664000175000017500000000000000000000000026623 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/common/0000775000175000017500000000000000000000000026014 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/common/__init__.py0000664000175000017500000000000000000000000030113 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/common/helpers.py0000664000175000017500000000272500000000000030036 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Development Co # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context from neutron.tests.common import helpers from neutron_dynamic_routing.services.bgp.common import constants as bgp_const def _get_bgp_dragent_dict(host): agent = { 'binary': 'neutron-bgp-dragent', 'host': host, 'topic': 'q-bgp_dragent', 'agent_type': bgp_const.AGENT_TYPE_BGP_ROUTING, 'configurations': {'bgp_speakers': 1}} return agent def register_bgp_dragent(host=helpers.HOST, admin_state_up=True, alive=True): agent = helpers._register_agent( _get_bgp_dragent_dict(host)) if not admin_state_up: helpers.set_agent_admin_state(agent['id']) if not alive: helpers.kill_agent(agent['id']) return helpers.FakePlugin()._get_agent_by_type_and_host( context.get_admin_context(), agent['agent_type'], agent['host']) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/0000775000175000017500000000000000000000000026666 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/__init__.py0000664000175000017500000000000000000000000030765 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/0000775000175000017500000000000000000000000030511 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/__init__.py0000664000175000017500000000000000000000000032610 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/0000775000175000017500000000000000000000000031261 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/__init__.py0000664000175000017500000000000000000000000033360 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/0000775000175000017500000000000000000000000033237 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/__init__.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/__ini0000664000175000017500000000000000000000000034225 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023400000000000011454 xustar0000000000000000134 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/test_bgp_dragent_scheduler.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/functional/services/bgp/scheduler/test_0000664000175000017500000002010500000000000034276 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import context import testscenarios from neutron.db import agents_db from neutron.tests.unit import testlib_api from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.services.bgp.scheduler import bgp_dragent_scheduler as bgp_dras # noqa from neutron_dynamic_routing.tests.common import helpers # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios class TestAutoSchedule(testlib_api.SqlTestCase, bgp_dras_db.BgpDrAgentSchedulerDbMixin, agents_db.AgentDbMixin): """Test various scenarios for schedule_unscheduled_bgp_speakers. Below is the brief description of the scenario variables -------------------------------------------------------- host_count number of hosts. agent_count number of BGP dynamic routing agents. down_agent_count number of DRAgents which are inactive. bgp_speaker_count Number of bgp_speakers. hosted_bgp_speakers A mapping of agent id to the ids of the bgp_speakers that they should be initially hosting. expected_schedule_return_value Expected return value of 'schedule_unscheduled_bgp_speakers'. expected_hosted_bgp_speakers This stores the expected bgp_speakers that should have been scheduled (or that could have already been scheduled) for each agent after the 'schedule_unscheduled_bgp_speakers' function is called. """ scenarios = [ ('No BgpDrAgent scheduled, if no DRAgent is present', dict(host_count=1, agent_count=0, down_agent_count=0, bgp_speaker_count=1, hosted_bgp_speakers={}, expected_schedule_return_value=False)), ('No BgpDrAgent scheduled, if no BGP speaker are present', dict(host_count=1, agent_count=1, down_agent_count=0, bgp_speaker_count=0, hosted_bgp_speakers={}, expected_schedule_return_value=False, expected_hosted_bgp_speakers={'agent-0': []})), ('No BgpDrAgent scheduled, if BGP speaker already hosted', dict(host_count=1, agent_count=1, down_agent_count=0, bgp_speaker_count=1, hosted_bgp_speakers={'agent-0': ['bgp-speaker-0']}, expected_schedule_return_value=False, expected_hosted_bgp_speakers={'agent-0': ['bgp-speaker-0']})), ('BgpDrAgent scheduled to the speaker, if the speaker is not hosted', dict(host_count=1, agent_count=1, down_agent_count=0, bgp_speaker_count=1, hosted_bgp_speakers={}, expected_schedule_return_value=True, expected_hosted_bgp_speakers={'agent-0': ['bgp-speaker-0']})), ('No BgpDrAgent scheduled, if all the agents are down', dict(host_count=2, agent_count=2, down_agent_count=2, bgp_speaker_count=1, hosted_bgp_speakers={}, expected_schedule_return_value=False, expected_hosted_bgp_speakers={'agent-0': [], 'agent-1': [], })), ] def _strip_host_index(self, name): """Strips the host index. Eg. if name = '2-agent-3', then 'agent-3' is returned. """ return name[name.find('-') + 1:] def _extract_index(self, name): """Extracts the index number and returns. Eg. if name = '2-agent-3', then 3 is returned """ return int(name.split('-')[-1]) def _get_hosted_bgp_speakers_on_dragent(self, agent_id): query = self.ctx.session.query( bgp_dras_db.BgpSpeakerDrAgentBinding.bgp_speaker_id) query = query.filter( bgp_dras_db.BgpSpeakerDrAgentBinding.agent_id == agent_id) return [item[0] for item in query] def _create_and_set_agents_down(self, hosts, agent_count=0, down_agent_count=0, admin_state_up=True): agents = [] if agent_count: for i, host in enumerate(hosts): is_alive = i >= down_agent_count agents.append(helpers.register_bgp_dragent( host, admin_state_up=admin_state_up, alive=is_alive)) return agents def _save_bgp_speakers(self, bgp_speakers): cls = bgp_db.BgpDbMixin() bgp_speaker_body = { 'bgp_speaker': {'name': 'fake_bgp_speaker', 'ip_version': '4', 'local_as': '123', 'advertise_floating_ip_host_routes': False, 'advertise_tenant_networks': False, 'peers': [], 'networks': []}} i = 1 for bgp_speaker_id in bgp_speakers: bgp_speaker_body['bgp_speaker']['local_as'] = i cls._save_bgp_speaker(self.ctx, bgp_speaker_body, uuid=bgp_speaker_id) i = i + 1 def _test_auto_schedule(self, host_index): scheduler = bgp_dras.ChanceScheduler() self.ctx = context.get_admin_context() msg = 'host_index = %s' % host_index # create hosts hosts = ['%s-agent-%s' % (host_index, i) for i in range(self.host_count)] bgp_dragents = self._create_and_set_agents_down(hosts, self.agent_count, self.down_agent_count) # create bgp_speakers self._bgp_speakers = ['%s-bgp-speaker-%s' % (host_index, i) for i in range(self.bgp_speaker_count)] self._save_bgp_speakers(self._bgp_speakers) # pre schedule the bgp_speakers to the agents defined in # self.hosted_bgp_speakers before calling auto_schedule_bgp_speaker for agent, bgp_speakers in self.hosted_bgp_speakers.items(): agent_index = self._extract_index(agent) for bgp_speaker in bgp_speakers: bs_index = self._extract_index(bgp_speaker) scheduler.bind(self.ctx, [bgp_dragents[agent_index]], self._bgp_speakers[bs_index]) retval = scheduler.schedule_unscheduled_bgp_speakers(self.ctx, hosts[host_index]) self.assertEqual(self.expected_schedule_return_value, retval, message=msg) if self.agent_count: agent_id = bgp_dragents[host_index].id hosted_bgp_speakers = self._get_hosted_bgp_speakers_on_dragent( agent_id) hosted_bs_ids = [self._strip_host_index(net) for net in hosted_bgp_speakers] expected_hosted_bgp_speakers = self.expected_hosted_bgp_speakers[ 'agent-%s' % host_index] self.assertCountEqual(hosted_bs_ids, expected_hosted_bgp_speakers, msg) def test_auto_schedule(self): for i in range(self.host_count): self._test_auto_schedule(i) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/0000775000175000017500000000000000000000000025503 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/__init__.py0000664000175000017500000000000000000000000027602 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/0000775000175000017500000000000000000000000026254 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/__init__.py0000664000175000017500000000000000000000000030353 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/0000775000175000017500000000000000000000000027040 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/__init__.py0000664000175000017500000000000000000000000031137 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/0000775000175000017500000000000000000000000032061 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/__init__.py0000664000175000017500000000000000000000000034160 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022500000000000011454 xustar0000000000000000127 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/test_bgp_dr_rpc_agent_api.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/test_bgp_dr0000664000175000017500000000745600000000000034314 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from unittest import mock from neutron.tests import base from neutron_lib import context from neutron_dynamic_routing.api.rpc.agentnotifiers import bgp_dr_rpc_agent_api class TestBgpDrAgentNotifyApi(base.BaseTestCase): def setUp(self): super(TestBgpDrAgentNotifyApi, self).setUp() self.notifier = ( bgp_dr_rpc_agent_api.BgpDrAgentNotifyApi()) mock_cast_p = mock.patch.object(self.notifier, '_notification_host_cast') self.mock_cast = mock_cast_p.start() mock_call_p = mock.patch.object(self.notifier, '_notification_host_call') self.mock_call = mock_call_p.start() self.context = context.get_admin_context() self.host = 'host-1' def test_agent_updated(self): admin_state_up = True host = 'my-hostname' self.notifier.agent_updated(self.context, admin_state_up, host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_dragent_bgp_routes_advertisement(self): bgp_speaker_id = 'bgp-speaker-1' routes = [{'destination': '1.1.1.1', 'next_hop': '2.2.2.2'}] self.notifier.bgp_routes_advertisement(self.context, bgp_speaker_id, routes, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_dragent_bgp_routes_withdrawal(self): bgp_speaker_id = 'bgp-speaker-1' routes = [{'destination': '1.1.1.1'}] self.notifier.bgp_routes_withdrawal(self.context, bgp_speaker_id, routes, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_peer_disassociated(self): bgp_speaker_id = 'bgp-speaker-1' bgp_peer_ip = '1.1.1.1' self.notifier.bgp_peer_disassociated(self.context, bgp_speaker_id, bgp_peer_ip, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_peer_associated(self): bgp_speaker_id = 'bgp-speaker-1' bgp_peer_id = 'bgp-peer-1' self.notifier.bgp_peer_associated(self.context, bgp_speaker_id, bgp_peer_id, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_speaker_created(self): bgp_speaker_id = 'bgp-speaker-1' self.notifier.bgp_speaker_created(self.context, bgp_speaker_id, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) def test_notify_bgp_speaker_removed(self): bgp_speaker_id = 'bgp-speaker-1' self.notifier.bgp_speaker_removed(self.context, bgp_speaker_id, self.host) self.assertEqual(1, self.mock_cast.call_count) self.assertEqual(0, self.mock_call.call_count) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/0000775000175000017500000000000000000000000030640 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/__init__.py0000664000175000017500000000000000000000000032737 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/test_bgp_speaker_rpc.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/api/rpc/handlers/test_bgp_speaker_0000664000175000017500000000331600000000000034246 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from unittest import mock from neutron.tests import base from neutron_lib.api.definitions import bgp as bgp_ext from neutron_lib.plugins import directory from neutron_dynamic_routing.api.rpc.handlers import bgp_speaker_rpc class TestBgpSpeakerRpcCallback(base.BaseTestCase): def setUp(self): super(TestBgpSpeakerRpcCallback, self).setUp() self.plugin = mock.Mock() directory.add_plugin(bgp_ext.ALIAS, self.plugin) self.callback = bgp_speaker_rpc.BgpSpeakerRpcCallback() def test_get_bgp_speaker_info(self): self.callback.get_bgp_speaker_info(mock.Mock(), bgp_speaker_id='id1') self.assertIsNotNone(len(self.plugin.mock_calls)) def test_get_bgp_peer_info(self): self.callback.get_bgp_peer_info(mock.Mock(), bgp_peer_id='id1') self.assertIsNotNone(len(self.plugin.mock_calls)) def test_get_bgp_speakers(self): self.callback.get_bgp_speakers(mock.Mock(), host='host') self.assertIsNotNone(len(self.plugin.mock_calls)) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/db/0000775000175000017500000000000000000000000026070 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/db/__init__.py0000664000175000017500000000000000000000000030167 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/db/test_bgp_db.py0000664000175000017500000023762200000000000030732 0ustar00zuulzuul00000000000000# Copyright 2016 Hewlett Packard Enterprise Development Company LP # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib from unittest import mock import netaddr from neutron.db import l3_dvr_ha_scheduler_db from neutron.tests.unit.extensions import test_l3 from neutron.tests.unit.plugins.ml2 import test_plugin from neutron_lib.api.definitions import external_net from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_const from neutron_lib import exceptions as n_exc from neutron_lib.plugins import constants as plugin_constants from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import uuidutils from neutron_dynamic_routing.extensions import bgp from neutron_dynamic_routing.services.bgp import bgp_plugin _uuid = uuidutils.generate_uuid ADVERTISE_FIPS_KEY = 'advertise_floating_ip_host_routes' IMAGINARY = '2b2334c8-adfe-42d9-82c6-ad866c7fc5d8' # non existent resource id class TestL3Plugin(test_l3.TestL3NatAgentSchedulingServicePlugin, l3_dvr_ha_scheduler_db.L3_DVR_HA_scheduler_db_mixin): pass class BgpEntityCreationMixin(object): @contextlib.contextmanager def bgp_speaker(self, ip_version, local_as, name='my-speaker', advertise_fip_host_routes=True, advertise_tenant_networks=True, networks=None, peers=None): data = {'ip_version': ip_version, ADVERTISE_FIPS_KEY: advertise_fip_host_routes, 'advertise_tenant_networks': advertise_tenant_networks, 'local_as': local_as, 'name': name} bgp_speaker = self.bgp_plugin.create_bgp_speaker(self.context, {'bgp_speaker': data}) bgp_speaker_id = bgp_speaker['id'] if networks: for network_id in networks: self.bgp_plugin.add_gateway_network( self.context, bgp_speaker_id, {'network_id': network_id}) if peers: for peer_id in peers: self.bgp_plugin.add_bgp_peer(self.context, bgp_speaker_id, {'bgp_peer_id': peer_id}) yield self.bgp_plugin.get_bgp_speaker(self.context, bgp_speaker_id) @contextlib.contextmanager def bgp_peer(self, tenant_id=_uuid(), remote_as='4321', peer_ip="192.168.1.1", auth_type="md5", password="my-secret", name="my-peer"): data = {'peer_ip': peer_ip, 'tenant_id': tenant_id, 'remote_as': remote_as, 'auth_type': auth_type, 'password': password, 'name': name} bgp_peer = self.bgp_plugin.create_bgp_peer(self.context, {'bgp_peer': data}) yield bgp_peer self.bgp_plugin.delete_bgp_peer(self.context, bgp_peer['id']) @contextlib.contextmanager def bgp_speaker_with_gateway_network(self, address_scope_id, local_as, advertise_fip_host_routes=True, advertise_tenant_networks=True, network_external=True, fmt=None, set_context=False): pass @contextlib.contextmanager def bgp_speaker_with_router(self, address_scope_id, local_as, gw_network_id=None, gw_subnet_ids=None, tenant_subnet_ids=None, advertise_fip_host_routes=True, advertise_tenant_networks=True, fmt=None, set_context=False, router_distributed=False): pass @contextlib.contextmanager def gw_network(self, name='ext-net', external=False, **kwargs): with self.network(name=name, **kwargs) as gw_network: if external: self._update('networks', gw_network['network']['id'], {'network': {external_net.EXTERNAL: True}}) yield gw_network @contextlib.contextmanager def router(self, name='bgp-test-router', tenant_id=_uuid(), admin_state_up=True, **kwargs): request = {'router': {'tenant_id': tenant_id, 'name': name, 'admin_state_up': admin_state_up}} for arg in kwargs: request['router'][arg] = kwargs[arg] router = self.l3plugin.create_router(self.context, request) yield router @contextlib.contextmanager def router_with_external_and_tenant_networks( self, tenant_id=_uuid(), gw_prefix='8.8.8.0/24', tenant_prefix='192.168.0.0/16', address_scope=None, distributed=False, ha=False, ext_net_use_addr_scope=True, tenant_net_use_addr_scope=True): gw_ip_net = netaddr.IPNetwork(gw_prefix) tenant_ip_net = netaddr.IPNetwork(tenant_prefix) ext_pool_args = {'tenant_id': tenant_id, 'name': 'bgp-pool'} tenant_pool_args = ext_pool_args.copy() if address_scope and ext_net_use_addr_scope: ext_pool_args['address_scope_id'] = address_scope['id'] if address_scope and tenant_net_use_addr_scope: tenant_pool_args['address_scope_id'] = address_scope['id'] with self.gw_network(external=True) as ext_net,\ self.network() as int_net,\ self.subnetpool([gw_prefix], **ext_pool_args) as ext_pool,\ self.subnetpool([tenant_prefix], **tenant_pool_args) as int_pool: ext_subnetpool_id = ext_pool['subnetpool']['id'] int_subnetpool_id = int_pool['subnetpool']['id'] gw_net_id = ext_net['network']['id'] with self.subnet(ext_net, cidr=gw_prefix, subnetpool_id=ext_subnetpool_id, ip_version=gw_ip_net.version),\ self.subnet(int_net, cidr=tenant_prefix, subnetpool_id=int_subnetpool_id, ip_version=tenant_ip_net.version) as int_subnet: ext_gw_info = {'network_id': gw_net_id} with self.router(external_gateway_info=ext_gw_info, distributed=distributed, ha=ha) as router: router_id = router['id'] router_interface_info = {'subnet_id': int_subnet['subnet']['id']} self.l3plugin.add_router_interface(self.context, router_id, router_interface_info) yield router, ext_net, int_net class BgpTests(BgpEntityCreationMixin): @contextlib.contextmanager def subnetpool_with_address_scope(self, ip_version, prefixes=None, shared=False, admin=True, name='test-pool', is_default_pool=False, tenant_id=None, **kwargs): if not tenant_id: tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': ip_version, 'shared': shared, 'name': name + '-scope'} address_scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) address_scope_id = address_scope['id'] pool_data = {'tenant_id': tenant_id, 'shared': shared, 'name': name, 'address_scope_id': address_scope_id, 'prefixes': prefixes, 'is_default': is_default_pool} for key in kwargs: pool_data[key] = kwargs[key] yield self.plugin.create_subnetpool(self.context, {'subnetpool': pool_data}) @contextlib.contextmanager def floatingip_from_address_scope_assoc(self, prefixes, address_scope_id, ext_prefixlen=24, int_prefixlen=24): pass def test_add_duplicate_bgp_peer_ip(self): peer_ip = '192.168.1.10' with self.bgp_peer(peer_ip=peer_ip) as peer1,\ self.bgp_peer(peer_ip=peer_ip) as peer2,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234, peers=[peer1['id']]) as speaker: self.assertRaises(bgp.DuplicateBgpPeerIpException, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': peer2['id']}) def test_bgpspeaker_create(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: speaker_name = 'test-speaker' expected_values = [('ip_version', sp['ip_version']), ('name', speaker_name)] with self.bgp_speaker(sp['ip_version'], 1234, name=speaker_name) as bgp_speaker: for k, v in expected_values: self.assertEqual(v, bgp_speaker[k]) def test_bgp_speaker_list(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp1,\ self.subnetpool_with_address_scope(4, prefixes=['9.0.0.0/8']) as sp2: with self.bgp_speaker(sp1['ip_version'], 1234, name='speaker1'),\ self.bgp_speaker(sp2['ip_version'], 4321, name='speaker2'): speakers = self.bgp_plugin.get_bgp_speakers(self.context) self.assertEqual(2, len(speakers)) def test_bgp_speaker_update_local_as(self): local_as_1 = 1234 local_as_2 = 4321 with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], local_as_1) as speaker: self.assertEqual(local_as_1, speaker['local_as']) new_speaker = self.bgp_plugin.update_bgp_speaker( self.context, speaker['id'], {'bgp_speaker': {'local_as': local_as_2}}) self.assertEqual(local_as_2, new_speaker['local_as']) def test_bgp_speaker_show_non_existent(self): self.assertRaises(bgp.BgpSpeakerNotFound, self.bgp_plugin.get_bgp_speaker, self.context, _uuid()) def test_create_bgp_peer(self): args = {'tenant_id': _uuid(), 'remote_as': '1111', 'peer_ip': '10.10.10.10', 'auth_type': 'md5'} with self.bgp_peer(tenant_id=args['tenant_id'], remote_as=args['remote_as'], peer_ip=args['peer_ip'], auth_type='md5', password='my-secret') as peer: self.assertIsNone(peer.get('password')) for key in args: self.assertEqual(args[key], peer[key]) def test_update_bgp_peer_auth_type_none(self): args = {'tenant_id': _uuid(), 'remote_as': '1111', 'peer_ip': '10.10.10.10', 'auth_type': 'md5'} with self.bgp_peer(tenant_id=args['tenant_id'], remote_as=args['remote_as'], peer_ip=args['peer_ip'], auth_type='none', name="my-peer") as peer: data = {'bgp_peer': {'password': "my-secret", 'name': "my-peer1"}} self.assertRaises(bgp.BgpPeerNotAuthenticated, self.bgp_plugin.update_bgp_peer, self.context, peer['id'], data) def test_update_bgp_peer_password_none(self): args = {'tenant_id': _uuid(), 'remote_as': 1111, 'peer_ip': '10.10.10.10', 'name': 'my-peer', 'auth_type': 'none'} with self.bgp_peer(tenant_id=args['tenant_id'], remote_as=args['remote_as'], peer_ip=args['peer_ip'], auth_type=args['auth_type'], name=args['name']) as peer: data = {'bgp_peer': {'name': "my-peer1"}} updated_peer = self.bgp_plugin.update_bgp_peer(self.context, peer['id'], data) for key in args: if key == 'name': self.assertEqual('my-peer1', updated_peer[key]) else: self.assertEqual(peer[key], updated_peer[key]) def test_bgp_peer_show_non_existent(self): self.assertRaises(bgp.BgpPeerNotFound, self.bgp_plugin.get_bgp_peer, self.context, 'unreal-bgp-peer-id') def test_associate_bgp_peer(self): with self.bgp_peer() as peer,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.bgp_plugin.add_bgp_peer(self.context, speaker['id'], {'bgp_peer_id': peer['id']}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertIn('peers', new_speaker) self.assertIn(peer['id'], new_speaker['peers']) self.assertEqual(1, len(new_speaker['peers'])) def test_remove_bgp_peer(self): with self.bgp_peer() as peer,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234, peers=[peer['id']]) as speaker: self.bgp_plugin.remove_bgp_peer(self.context, speaker['id'], {'bgp_peer_id': peer['id']}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertIn('peers', new_speaker) self.assertTrue(not new_speaker['peers']) def test_remove_unassociated_bgp_peer(self): with self.bgp_peer() as peer,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpSpeakerPeerNotAssociated, self.bgp_plugin.remove_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': peer['id']}) def test_remove_non_existent_bgp_peer(self): bgp_peer_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpSpeakerPeerNotAssociated, self.bgp_plugin.remove_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': bgp_peer_id}) def test_add_non_existent_bgp_peer(self): bgp_peer_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpPeerNotFound, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': bgp_peer_id}) def test_add_bgp_peer_without_id(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.BadRequest, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {}) def test_add_bgp_peer_with_bad_id(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.BadRequest, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': 'aaa'}) def test_add_bgp_peer_with_none_id(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.BadRequest, self.bgp_plugin.add_bgp_peer, self.context, speaker['id'], {'bgp_peer_id': None}) def test_add_gateway_network(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker,\ self.gw_network() as network: network_id = network['network']['id'] self.bgp_plugin.add_gateway_network(self.context, speaker['id'], {'network_id': network_id}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertEqual(1, len(new_speaker['networks'])) self.assertTrue(network_id in new_speaker['networks']) def test_create_bgp_speaker_with_network(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp,\ self.gw_network(name='test-net', tenant_id=_uuid(), shared=True) as network: network_id = network['network']['id'] with self.bgp_speaker(sp['ip_version'], 1234, networks=[network_id]) as speaker: self.assertEqual(1, len(speaker['networks'])) self.assertTrue(network_id in speaker['networks']) def test_remove_gateway_network(self): with self.gw_network() as network1,\ self.gw_network() as network2,\ self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: network1_id = network1['network']['id'] network2_id = network2['network']['id'] with self.bgp_speaker(sp['ip_version'], 1234, networks=[network1_id, network2_id]) as speaker: self.bgp_plugin.remove_gateway_network( self.context, speaker['id'], {'network_id': network1_id}) new_speaker = self.bgp_plugin.get_bgp_speaker(self.context, speaker['id']) self.assertEqual(1, len(new_speaker['networks'])) def test_add_non_existent_gateway_network(self): network_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(n_exc.NetworkNotFound, self.bgp_plugin.add_gateway_network, self.context, speaker['id'], {'network_id': network_id}) def test_remove_non_existent_gateway_network(self): network_id = IMAGINARY with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker: self.assertRaises(bgp.BgpSpeakerNetworkNotAssociated, self.bgp_plugin.remove_gateway_network, self.context, speaker['id'], {'network_id': network_id}) def test_add_gateway_network_two_bgp_speakers_same_scope(self): with self.subnetpool_with_address_scope(4, prefixes=['8.0.0.0/8']) as sp: with self.bgp_speaker(sp['ip_version'], 1234) as speaker1,\ self.bgp_speaker(sp['ip_version'], 4321) as speaker2,\ self.gw_network() as network: network_id = network['network']['id'] self.bgp_plugin.add_gateway_network(self.context, speaker1['id'], {'network_id': network_id}) self.bgp_plugin.add_gateway_network(self.context, speaker2['id'], {'network_id': network_id}) speaker1 = self.bgp_plugin.get_bgp_speaker(self.context, speaker1['id']) speaker2 = self.bgp_plugin.get_bgp_speaker(self.context, speaker2['id']) for speaker in [speaker1, speaker2]: self.assertEqual(1, len(speaker['networks'])) self.assertEqual(network_id, speaker['networks'][0]) def test_create_bgp_peer_md5_auth_no_password(self): bgp_peer = {'bgp_peer': {'auth_type': 'md5', 'password': None}} self.assertRaises(bgp.InvalidBgpPeerMd5Authentication, self.bgp_plugin.create_bgp_peer, self.context, bgp_peer) def test__get_address_scope_ids_for_bgp_speaker(self): prefixes1 = ['8.0.0.0/8'] prefixes2 = ['9.0.0.0/8'] prefixes3 = ['10.0.0.0/8'] tenant_id = _uuid() with self.bgp_speaker(4, 1234) as speaker,\ self.subnetpool_with_address_scope(4, prefixes=prefixes1, tenant_id=tenant_id) as sp1,\ self.subnetpool_with_address_scope(4, prefixes=prefixes2, tenant_id=tenant_id) as sp2,\ self.subnetpool_with_address_scope(4, prefixes=prefixes3, tenant_id=tenant_id) as sp3,\ self.gw_network() as network1, self.gw_network() as network2,\ self.gw_network() as network3: network1_id = network1['network']['id'] network2_id = network2['network']['id'] network3_id = network3['network']['id'] base_subnet_data = { 'allocation_pools': n_const.ATTR_NOT_SPECIFIED, 'cidr': n_const.ATTR_NOT_SPECIFIED, 'prefixlen': n_const.ATTR_NOT_SPECIFIED, 'ip_version': 4, 'enable_dhcp': True, 'dns_nameservers': n_const.ATTR_NOT_SPECIFIED, 'host_routes': n_const.ATTR_NOT_SPECIFIED} subnet1_data = {'network_id': network1_id, 'subnetpool_id': sp1['id'], 'name': 'subnet1', 'tenant_id': tenant_id} subnet2_data = {'network_id': network2_id, 'subnetpool_id': sp2['id'], 'name': 'subnet2', 'tenant_id': tenant_id} subnet3_data = {'network_id': network3_id, 'subnetpool_id': sp3['id'], 'name': 'subnet2', 'tenant_id': tenant_id} for k in base_subnet_data: subnet1_data[k] = base_subnet_data[k] subnet2_data[k] = base_subnet_data[k] subnet3_data[k] = base_subnet_data[k] self.plugin.create_subnet(self.context, {'subnet': subnet1_data}) self.plugin.create_subnet(self.context, {'subnet': subnet2_data}) self.plugin.create_subnet(self.context, {'subnet': subnet3_data}) self.bgp_plugin.add_gateway_network(self.context, speaker['id'], {'network_id': network1_id}) self.bgp_plugin.add_gateway_network(self.context, speaker['id'], {'network_id': network2_id}) scopes = self.bgp_plugin._get_address_scope_ids_for_bgp_speaker( self.context, speaker['id']) self.assertEqual(2, len(scopes)) self.assertTrue(sp1['address_scope_id'] in scopes) self.assertTrue(sp2['address_scope_id'] in scopes) def test_get_routes_by_bgp_speaker_binding(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding( self.context, bgp_speaker_id, gw_net_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(tenant_prefix, routes[0]['destination']) self.assertEqual(next_hop, routes[0]['next_hop']) def test_get_routes_by_binding_network(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding( self.context, bgp_speaker_id, gw_net_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(tenant_prefix, routes[0]['destination']) self.assertEqual(next_hop, routes[0]['next_hop']) def _advertised_routes_by_bgp_speaker(self, bgp_speaker_ip_version, local_as, tenant_cidr, gateway_cidr, fip_routes=True, router_distributed=False): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': bgp_speaker_ip_version, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gateway_cidr, tenant_prefix=tenant_cidr, address_scope=scope, distributed=router_distributed) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] with self.bgp_speaker( bgp_speaker_ip_version, local_as, networks=[gw_net_id], advertise_fip_host_routes=fip_routes) as speaker: routes = self.bgp_plugin.get_advertised_routes( self.context, speaker['id']) return routes['advertised_routes'] def test__tenant_prefixes_by_router_no_gateway_port(self): with self.network() as net1, self.network() as net2,\ self.subnetpool_with_address_scope(6, tenant_id=_uuid(), prefixes=['2001:db8::/63']) as pool: subnetpool_id = pool['id'] with self.subnet(network=net1, cidr=None, subnetpool_id=subnetpool_id, ip_version=6) as ext_subnet,\ self.subnet(network=net2, cidr=None, subnetpool_id=subnetpool_id, ip_version=6) as int_subnet,\ self.router() as router: router_id = router['id'] int_subnet_id = int_subnet['subnet']['id'] ext_subnet_id = ext_subnet['subnet']['id'] self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': int_subnet_id}) self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': ext_subnet_id}) with self.bgp_speaker(6, 1234) as speaker: bgp_speaker_id = speaker['id'] cidrs = list(self.bgp_plugin._tenant_prefixes_by_router( self.context, router_id, bgp_speaker_id)) self.assertFalse(cidrs) def test_get_ipv6_tenant_subnet_routes_by_bgp_speaker_ipv6(self): tenant_cidr = '2001:db8::/64' binding_cidr = '2001:ab8::/64' routes = self._advertised_routes_by_bgp_speaker(6, 1234, tenant_cidr, binding_cidr) self.assertEqual(1, len(routes)) dest_prefix = routes[0]['destination'] next_hop = routes[0]['next_hop'] self.assertEqual(tenant_cidr, dest_prefix) self.assertTrue(netaddr.IPSet([binding_cidr]).__contains__(next_hop)) def test_get_ipv4_tenant_subnet_routes_by_bgp_speaker_ipv4(self): tenant_cidr = '172.16.10.0/24' binding_cidr = '20.10.1.0/24' routes = self._advertised_routes_by_bgp_speaker(4, 1234, tenant_cidr, binding_cidr) routes = list(routes) self.assertEqual(1, len(routes)) dest_prefix = routes[0]['destination'] next_hop = routes[0]['next_hop'] self.assertEqual(tenant_cidr, dest_prefix) self.assertTrue(netaddr.IPSet([binding_cidr]).__contains__(next_hop)) def test_get_ipv4_tenant_subnet_routes_by_bgp_speaker_dvr_router(self): tenant_cidr = '172.16.10.0/24' binding_cidr = '20.10.1.0/24' routes = self._advertised_routes_by_bgp_speaker( 4, 1234, tenant_cidr, binding_cidr, router_distributed=True) routes = list(routes) self.assertEqual(1, len(routes)) def test_all_routes_by_bgp_speaker_different_tenant_address_scope(self): binding_cidr = '2001:db8::/64' tenant_cidr = '2002:ab8::/64' with self.subnetpool_with_address_scope(6, tenant_id=_uuid(), prefixes=[binding_cidr]) as ext_pool,\ self.subnetpool_with_address_scope(6, tenant_id=_uuid(), prefixes=[tenant_cidr]) as int_pool,\ self.gw_network(external=True) as ext_net,\ self.network() as int_net: gw_net_id = ext_net['network']['id'] ext_pool_id = ext_pool['id'] int_pool_id = int_pool['id'] with self.subnet(cidr=None, subnetpool_id=ext_pool_id, network=ext_net, ip_version=6) as ext_subnet,\ self.subnet(cidr=None, subnetpool_id=int_pool_id, network=int_net, ip_version=6) as int_subnet,\ self.router() as router: router_id = router['id'] int_subnet_id = int_subnet['subnet']['id'] ext_subnet_id = ext_subnet['subnet']['id'] self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': int_subnet_id}) self.l3plugin.add_router_interface(self.context, router_id, {'subnet_id': ext_subnet_id}) with self.bgp_speaker(6, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] cidrs = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) self.assertEqual(0, len(list(cidrs))) def test__get_routes_by_router_with_fip(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_routes_by_router(self.context, router['id']) routes = routes[bgp_speaker_id] next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(2, len(routes)) tenant_prefix_found = False fip_prefix_found = False for route in routes: self.assertEqual(next_hop, route['next_hop']) if route['destination'] == tenant_prefix: tenant_prefix_found = True if route['destination'] == fip_prefix: fip_prefix_found = True self.assertTrue(tenant_prefix_found) self.assertTrue(fip_prefix_found) def test_get_routes_by_bgp_speaker_id_with_fip(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(2, len(routes)) tenant_prefix_found = False fip_prefix_found = False for route in routes: self.assertEqual(next_hop, route['next_hop']) if route['destination'] == tenant_prefix: tenant_prefix_found = True if route['destination'] == fip_prefix: fip_prefix_found = True self.assertTrue(tenant_prefix_found) self.assertTrue(fip_prefix_found) def test_get_routes_by_bgp_speaker_id_with_fip_dvr(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' fixed_prefix = fixed_port['fixed_ips'][0]['ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) routes = list(routes) cvr_gw_ip = ext_gw_info['external_fixed_ips'][0]['ip_address'] dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] self.assertEqual(3, len(routes)) tenant_route_verified = False fip_route_verified = False fixed_ip_route_verified = False for route in routes: if route['destination'] == tenant_prefix: self.assertEqual(cvr_gw_ip, route['next_hop']) tenant_route_verified = True if route['destination'] == fip_prefix: self.assertEqual(dvr_gw_ip, route['next_hop']) fip_route_verified = True if route['destination'] == fixed_prefix: self.assertEqual(dvr_gw_ip, route['next_hop']) fixed_ip_route_verified = True self.assertTrue(tenant_route_verified) self.assertTrue(fip_route_verified) self.assertTrue(fixed_ip_route_verified) def test__get_dvr_fip_host_routes_by_binding(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_dvr_fip_host_routes_by_binding( self.context, gw_net_id, bgp_speaker_id) routes = list(routes) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(dvr_gw_ip, routes[0]['next_hop']) self.assertEqual(fip_prefix, routes[0]['destination']) def test__get_dvr_fip_host_routes_by_router(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_dvr_fip_host_routes_by_router( self.context, bgp_speaker_id, router['id']) routes = list(routes) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] self.assertEqual(1, len(routes)) self.assertEqual(dvr_gw_ip, routes[0]['next_hop']) self.assertEqual(fip_prefix, routes[0]['destination']) def test_get_routes_by_bgp_speaker_binding_with_fip(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': fixed_port['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_prefix = fip['floating_ip_address'] + '/32' with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_binding( self.context, bgp_speaker_id, gw_net_id) routes = list(routes) next_hop = ext_gw_info['external_fixed_ips'][0]['ip_address'] self.assertEqual(2, len(routes)) tenant_prefix_found = False fip_prefix_found = False for route in routes: self.assertEqual(next_hop, route['next_hop']) if route['destination'] == tenant_prefix: tenant_prefix_found = True if route['destination'] == fip_prefix: fip_prefix_found = True self.assertTrue(tenant_prefix_found) self.assertTrue(fip_prefix_found) def test__bgp_speakers_for_gateway_network_by_ip_version(self): with self.gw_network(external=True) as ext_net,\ self.bgp_speaker(6, 1234) as s1,\ self.bgp_speaker(6, 4321) as s2: gw_net_id = ext_net['network']['id'] self.bgp_plugin.add_gateway_network(self.context, s1['id'], {'network_id': gw_net_id}) self.bgp_plugin.add_gateway_network(self.context, s2['id'], {'network_id': gw_net_id}) speakers = self.bgp_plugin._bgp_speakers_for_gw_network_by_family( self.context, gw_net_id, 6) self.assertEqual(2, len(speakers)) def test__bgp_speakers_for_gateway_network_by_ip_version_no_binding(self): with self.gw_network(external=True) as ext_net,\ self.bgp_speaker(6, 1234),\ self.bgp_speaker(6, 4321): gw_net_id = ext_net['network']['id'] speakers = self.bgp_plugin._bgp_speakers_for_gw_network_by_family( self.context, gw_net_id, 6) self.assertTrue(not speakers) def _create_scenario_test_l3_agents(self, agent_confs): for item in agent_confs: self.plugin.create_or_update_agent( self.context, {'agent_type': 'L3 agent', 'host': item['host'], 'binary': 'neutron-l3-agent', 'topic': 'test', 'configurations': {"agent_mode": item['mode']}}) def _create_scenario_test_ports(self, tenant_id, port_configs): ports = [] for item in port_configs: port_data = { 'port': {'name': 'test1', 'network_id': item['net_id'], 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: item['host']}} port = self.plugin.create_port(self.context, port_data) ports.append(port) return ports def _create_scenario_test_fips(self, ext_net_id, tenant_id, port_ids): fips = [] for port_id in port_ids: fip_data = {'floatingip': {'floating_network_id': ext_net_id, 'tenant_id': tenant_id, 'port_id': port_id}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fips.append(fip) return fips def test_floatingip_update_callback(self): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() agent_confs = [{"host": "network1", "mode": "legacy"}] self._create_scenario_test_l3_agents(agent_confs) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] ext_gw_info = router['external_gateway_info'] next_hop = ext_gw_info[ 'external_fixed_ips'][0]['ip_address'] port_configs = [{'net_id': int_net['network']['id'], 'host': 'compute1'}] ports = self._create_scenario_test_ports(tenant_id, port_configs) port_ids = [port['id'] for port in ports] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: with mock.patch.object( self.bgp_plugin, 'start_route_advertisements') as start_adv: with mock.patch.object( self.bgp_plugin, 'stop_route_advertisements') as stop_adv: bgp_speaker_id = speaker['id'] # Create and associate floating IP fips = self._create_scenario_test_fips( gw_net_id, tenant_id, port_ids) fip_id = fips[0]['id'] fip_addr = fips[0]['floating_ip_address'] host_route = {'destination': fip_addr + '/32', 'next_hop': next_hop} start_adv.assert_called_once_with( mock.ANY, mock.ANY, bgp_speaker_id, [host_route]) fip_data = {'floatingip': {'port_id': None}} fip = self.l3plugin.update_floatingip( self.context, fip_id, fip_data) self.assertIsNone(fip['port_id']) # Dissociate floating IP, # withdraw route with next_hop None host_route['next_hop'] = None stop_adv.assert_called_once_with( mock.ANY, mock.ANY, bgp_speaker_id, [host_route]) def _test_legacy_router_fips_next_hop(self, router_ha=False): if router_ha: cfg.CONF.set_override('l3_ha', True) cfg.CONF.set_override('max_l3_agents_per_router', 2) gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() agent_confs = [{"host": "compute1", "mode": "dvr"}, {"host": "compute2", "mode": "dvr"}, {"host": "network1", "mode": "dvr_snat"}] if router_ha: agent_confs.append({"host": "network2", "mode": "dvr_snat"}) self._create_scenario_test_l3_agents(agent_confs) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, ha=router_ha) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] ext_gw_info = router['external_gateway_info'] self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'compute1') self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'compute2') port_configs = [{'net_id': int_net['network']['id'], 'host': 'compute1'}, {'net_id': int_net['network']['id'], 'host': 'compute2'}] ports = self._create_scenario_test_ports(tenant_id, port_configs) port_ids = [port['id'] for port in ports] self._create_scenario_test_fips(gw_net_id, tenant_id, port_ids) next_hop = ext_gw_info[ 'external_fixed_ips'][0]['ip_address'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin.get_routes_by_bgp_speaker_id( self.context, bgp_speaker_id) routes = list(routes) self.assertEqual(2, len(routes)) for route in routes: self.assertEqual(next_hop, route['next_hop']) def test_legacy_router_fips_has_no_next_hop_to_fip_agent_gateway(self): self._test_legacy_router_fips_next_hop() def test_ha_router_fips_has_no_next_hop_to_fip_agent_gateway(self): self._test_legacy_router_fips_next_hop(router_ha=True) def _test__get_fip_next_hop(self, distributed=False): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() agent_confs = [{"host": "compute1", "mode": "dvr"}, {"host": "network1", "mode": "dvr_snat"}] self._create_scenario_test_l3_agents(agent_confs) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, distributed=distributed) as res: router, ext_net, int_net = res ext_gw_info = router['external_gateway_info'] legacy_gw_ip = ext_gw_info[ 'external_fixed_ips'][0]['ip_address'] gw_net_id = ext_net['network']['id'] # Whatever the router type is, we always create such dvr fip agent # gateway port, in order to make sure that legacy router fip bgp # route next_hop is not the dvr_fip_agent_gw. fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'compute1') port_configs = [{'net_id': int_net['network']['id'], 'host': 'compute1'}] ports = self._create_scenario_test_ports(tenant_id, port_configs) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] fip_data = {'floatingip': {'floating_network_id': gw_net_id, 'tenant_id': tenant_id, 'port_id': ports[0]['id']}} fip = self.l3plugin.create_floatingip(self.context, fip_data) fip_address = fip['floating_ip_address'] with self.bgp_speaker(4, 1234, networks=[gw_net_id]): next_hop = self.bgp_plugin._get_fip_next_hop( self.context, router['id'], fip_address) if distributed: self.assertEqual(dvr_gw_ip, next_hop) else: self.assertEqual(legacy_gw_ip, next_hop) def test__get_fip_next_hop_legacy(self): self._test__get_fip_next_hop() def test__get_fip_next_hop_dvr(self): self._test__get_fip_next_hop(distributed=True) def _test__get_dvr_fixed_ip_routes_by_bgp_speaker(self, ext_use_scope, tenant_use_scope): gw_prefix = '172.16.10.0/24' tenant_prefix = '10.10.10.0/24' tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True, ext_net_use_addr_scope=ext_use_scope, tenant_net_use_addr_scope=tenant_use_scope) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) fixed_ip_prefix = fixed_port['fixed_ips'][0]['ip_address'] + '/32' self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) fip_gw = self.l3plugin.create_fip_agent_gw_port_if_not_exists( self.context, gw_net_id, 'test-host') with self.bgp_speaker(4, 1234, networks=[gw_net_id]) as speaker: bgp_speaker_id = speaker['id'] routes = self.bgp_plugin._get_dvr_fixed_ip_routes_by_bgp_speaker( # noqa self.context, bgp_speaker_id) routes = list(routes) dvr_gw_ip = fip_gw['fixed_ips'][0]['ip_address'] if ext_use_scope and tenant_use_scope: self.assertEqual(1, len(routes)) self.assertEqual(dvr_gw_ip, routes[0]['next_hop']) self.assertEqual(fixed_ip_prefix, routes[0]['destination']) else: self.assertEqual(0, len(routes)) def test__get_dvr_fixed_ip_routes_by_bgp_speaker_same_scope(self): self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(True, True) def test__get_dvr_fixed_ip_routes_by_bgp_speaker_different_scope(self): self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(True, False) self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(False, True) def test__get_dvr_fixed_ip_routes_by_bgp_speaker_no_scope(self): self._test__get_dvr_fixed_ip_routes_by_bgp_speaker(False, False) def test_get_external_networks_for_port_same_address_scope_v4(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, True) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, True, False) def test_get_external_networks_for_port_different_address_scope_v4(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 4, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, False) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, True, False, False) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, False, True) self._test_get_external_networks_for_port('172.10.2.0/24', '10.0.0.0/24', scope_data, tenant_id, False, True, False) def test_get_external_networks_for_port_same_address_scope_v6(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 6, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, True) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, True, False) def test_get_external_networks_for_port_different_address_scope_v6(self): tenant_id = _uuid() scope_data = {'tenant_id': tenant_id, 'ip_version': 6, 'shared': True, 'name': 'bgp-scope'} self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, False) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, True, False, False) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, False, True) self._test_get_external_networks_for_port('2001:1234:1234::/64', '2001:1234:4321::/64', scope_data, tenant_id, False, True, False) def _test_get_external_networks_for_port(self, gw_prefix, tenant_prefix, scope_data, tenant_id, external_use_address_scope, project_use_address_scope, match_address_scopes=True): scope = None if scope_data: scope = self.plugin.create_address_scope( self.context, {'address_scope': scope_data}) with self.router_with_external_and_tenant_networks( tenant_id=tenant_id, gw_prefix=gw_prefix, tenant_prefix=tenant_prefix, address_scope=scope, distributed=True, ext_net_use_addr_scope=external_use_address_scope, tenant_net_use_addr_scope=project_use_address_scope) as res: router, ext_net, int_net = res gw_net_id = ext_net['network']['id'] tenant_net_id = int_net['network']['id'] fixed_port_data = {'port': {'name': 'test', 'network_id': tenant_net_id, 'tenant_id': tenant_id, 'admin_state_up': True, 'device_id': _uuid(), 'device_owner': 'compute:nova', 'mac_address': n_const.ATTR_NOT_SPECIFIED, 'fixed_ips': n_const.ATTR_NOT_SPECIFIED, portbindings.HOST_ID: 'test-host'}} fixed_port = self.plugin.create_port(self.context, fixed_port_data) self.plugin.create_or_update_agent(self.context, {'agent_type': 'L3 agent', 'host': 'test-host', 'binary': 'neutron-l3-agent', 'topic': 'test'}) ext_nets = self.bgp_plugin.get_external_networks_for_port( self.context, fixed_port, match_address_scopes=match_address_scopes) if not match_address_scopes: self.assertEqual(1, len(ext_nets)) self.assertEqual(gw_net_id, ext_nets[0]) elif external_use_address_scope and project_use_address_scope: self.assertEqual(1, len(ext_nets)) self.assertEqual(gw_net_id, ext_nets[0]) else: self.assertEqual(0, len(ext_nets)) class Ml2BgpTests(test_plugin.Ml2PluginV2TestCase, BgpTests): fmt = 'json' def setup_parent(self): self.l3_plugin = ('neutron_dynamic_routing.tests.unit.db.test_bgp_db.' 'TestL3Plugin') super(Ml2BgpTests, self).setup_parent() def setUp(self): super(Ml2BgpTests, self).setUp() self.l3plugin = directory.get_plugin(plugin_constants.L3) self.bgp_plugin = bgp_plugin.BgpPlugin() self.plugin = directory.get_plugin() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/db/test_bgp_dragentscheduler_db.py0000664000175000017500000002117100000000000034323 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Hewlett Packard Enterprise Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.api.definitions import bgp from neutron_lib import context from neutron_lib.plugins import directory from oslo_config import cfg from oslo_utils import importutils from neutron.extensions import agent from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_base_plugin from neutron.tests.unit.extensions import test_agent from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.extensions import bgp_dragentscheduler as bgp_dras_ext # noqa from neutron_dynamic_routing.tests.common import helpers from neutron_dynamic_routing.tests.unit.db import test_bgp_db from webob import exc class BgpDrSchedulerTestExtensionManager(object): def get_resources(self): return (agent.Agent.get_resources() + bgp_dras_ext.Bgp_dragentscheduler.get_resources()) def get_actions(self): return [] def get_request_extensions(self): return [] class TestBgpDrSchedulerPlugin(bgp_db.BgpDbMixin, bgp_dras_db.BgpDrAgentSchedulerDbMixin): bgp_drscheduler = importutils.import_object( cfg.CONF.bgp_drscheduler_driver) supported_extension_aliases = ["bgp_dragent_scheduler"] def get_plugin_description(self): return ("BGP dynamic routing service Plugin test class that test " "BGP speaker functionality, with scheduler.") class BgpDrSchedulingTestCase(test_agent.AgentDBTestMixIn, test_bgp_db.BgpEntityCreationMixin): def test_schedule_bgp_speaker(self): """Test happy path over full scheduling cycle.""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] agent_id = agent['id'] data = {'bgp_speaker_id': bgp_speaker_id} req = self.new_create_request('agents', data, self.fmt, agent_id, 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) req_show = self.new_show_request('agents', agent_id, self.fmt, 'bgp-drinstances') res = req_show.get_response(self.ext_api) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(self.fmt, res) self.assertIn('bgp_speakers', res) self.assertTrue(bgp_speaker_id, res['bgp_speakers'][0]['id']) req = self.new_delete_request('agents', agent_id, self.fmt, 'bgp-drinstances', bgp_speaker_id) res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPNoContent.code, res.status_int) res = req_show.get_response(self.ext_api) self.assertEqual(exc.HTTPOk.code, res.status_int) res = self.deserialize(self.fmt, res) self.assertIn('bgp_speakers', res) self.assertEqual([], res['bgp_speakers']) def test_schedule_bgp_speaker_on_invalid_agent(self): """Test error while scheduling BGP speaker on an invalid agent.""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] self._register_l3_agent(host='host1') # Register wrong agent agent = self._list('agents')['agents'][0] data = {'bgp_speaker_id': bgp_speaker_id} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) # Raises an AgentNotFound exception if the agent is invalid self.assertEqual(exc.HTTPNotFound.code, res.status_int) def test_schedule_bgp_speaker_twice_on_same_agent(self): """Test error if a BGP speaker is scheduled twice on same agent""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] data = {'bgp_speaker_id': bgp_speaker_id} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) # Try second time, should raise conflict res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_schedule_bgp_speaker_on_two_different_agents(self): """Test that a BGP speaker can be associated to two agents.""" with self.bgp_speaker(4, 1234) as ri: bgp_speaker_id = ri['id'] helpers.register_bgp_dragent(host='host1') helpers.register_bgp_dragent(host='host2') data = {'bgp_speaker_id': bgp_speaker_id} agent1 = self._list('agents')['agents'][0] req = self.new_create_request( 'agents', data, self.fmt, agent1['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) agent2 = self._list('agents')['agents'][1] req = self.new_create_request( 'agents', data, self.fmt, agent2['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) def test_schedule_multi_bgp_speaker_on_one_dragent(self): """Test only one BGP speaker can be associated to one dragent.""" with self.bgp_speaker(4, 1) as ri1, self.bgp_speaker(4, 2) as ri2: helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] data = {'bgp_speaker_id': ri1['id']} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPCreated.code, res.status_int) data = {'bgp_speaker_id': ri2['id']} req = self.new_create_request( 'agents', data, self.fmt, agent['id'], 'bgp-drinstances') res = req.get_response(self.ext_api) self.assertEqual(exc.HTTPConflict.code, res.status_int) def test_non_scheduled_bgp_speaker_binding_removal(self): """Test exception while removing an invalid binding.""" with self.bgp_speaker(4, 1234) as ri1: helpers.register_bgp_dragent(host='host1') agent = self._list('agents')['agents'][0] agent_id = agent['id'] self.assertRaises(bgp_dras_ext.DrAgentNotHostingBgpSpeaker, self.bgp_plugin.remove_bgp_speaker_from_dragent, self.context, agent_id, ri1['id']) class BgpDrPluginSchedulerTests(test_db_base_plugin.NeutronDbPluginV2TestCase, BgpDrSchedulingTestCase): def setUp(self, plugin=None, ext_mgr=None, service_plugins=None): if not plugin: plugin = ('neutron_dynamic_routing.tests.unit.db.' 'test_bgp_dragentscheduler_db.TestBgpDrSchedulerPlugin') if not service_plugins: service_plugins = {bgp.ALIAS: 'neutron_dynamic_routing.services.bgp.' 'bgp_plugin.BgpPlugin'} ext_mgr = ext_mgr or BgpDrSchedulerTestExtensionManager() super(BgpDrPluginSchedulerTests, self).setUp( plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins) self.bgp_plugin = directory.get_plugin(bgp.ALIAS) self.context = context.get_admin_context() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/0000775000175000017500000000000000000000000027326 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/__init__.py0000664000175000017500000000000000000000000031425 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/0000775000175000017500000000000000000000000030076 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/__init__.py0000664000175000017500000000000000000000000032175 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/0000775000175000017500000000000000000000000031174 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/__init__.py0000664000175000017500000000000000000000000033273 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/test_bgp_dragent.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/agent/test_bgp_dragen0000664000175000017500000010257500000000000034260 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import copy import sys from unittest import mock import eventlet from neutron_lib import context from oslo_config import cfg from oslo_utils import uuidutils import testtools from neutron.common import config as n_config from neutron.conf.agent import common as config from neutron.tests import base from neutron_dynamic_routing.services.bgp.agent import bgp_dragent from neutron_dynamic_routing.services.bgp.agent import config as bgp_config HOSTNAME = 'hostname' rpc_api = bgp_dragent.BgpDrPluginApi BGP_PLUGIN = '%s.%s' % (rpc_api.__module__, rpc_api.__name__) FAKE_BGPSPEAKER_UUID = uuidutils.generate_uuid() FAKE_BGPPEER_UUID = uuidutils.generate_uuid() FAKE_BGP_SPEAKER = {'id': FAKE_BGPSPEAKER_UUID, 'local_as': 12345, 'peers': [{'remote_as': '2345', 'peer_ip': '1.1.1.1', 'auth_type': 'none', 'password': ''}], 'advertised_routes': []} FAKE_BGP_PEER = {'id': FAKE_BGPPEER_UUID, 'remote_as': '2345', 'peer_ip': '1.1.1.1', 'auth_type': 'none', 'password': ''} FAKE_ROUTE = {'id': FAKE_BGPSPEAKER_UUID, 'destination': '2.2.2.2/32', 'next_hop': '3.3.3.3'} FAKE_ROUTES = {'routes': {'id': FAKE_BGPSPEAKER_UUID, 'destination': '2.2.2.2/32', 'next_hop': '3.3.3.3'} } class TestBgpDrAgent(base.BaseTestCase): def setUp(self): super(TestBgpDrAgent, self).setUp() cfg.CONF.register_opts(bgp_config.BGP_DRIVER_OPTS, 'BGP') cfg.CONF.register_opts(bgp_config.BGP_PROTO_CONFIG_OPTS, 'BGP') cfg.CONF.register_opts(config.AGENT_STATE_OPTS, 'AGENT') mock_log_p = mock.patch.object(bgp_dragent, 'LOG') self.mock_log = mock_log_p.start() self.driver_cls_p = mock.patch( 'neutron_dynamic_routing.services.bgp.agent.bgp_dragent.' 'importutils.import_class') self.driver_cls = self.driver_cls_p.start() self.context = context.get_admin_context() @mock.patch('neutron.common.config.init') def test_bgp_dragent_manager(self, mock_init): mock_init.return_value = '/tmp/test' state_rpc_str = 'neutron.agent.rpc.PluginReportStateAPI' # sync_state is needed for this test with mock.patch.object(bgp_dragent.BgpDrAgentWithStateReport, 'sync_state', autospec=True) as mock_sync_state: with mock.patch(state_rpc_str) as state_rpc: test_args = [ 'bgp_dragent', '--config-file', base.etcdir('neutron.conf') ] with mock.patch.object(sys, 'argv', test_args): config.register_agent_state_opts_helper(cfg.CONF) n_config.init(sys.argv[1:]) agent_mgr = bgp_dragent.BgpDrAgentWithStateReport( 'testhost') eventlet.greenthread.sleep(1) agent_mgr.after_start() self.assertIsNotNone(len(mock_sync_state.mock_calls)) state_rpc.assert_has_calls( [mock.call(mock.ANY), mock.call().report_state(mock.ANY, mock.ANY, mock.ANY)]) def test_run_completes_single_pass(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, 'sync_state') as sync_state: bgp_dr.run() self.assertIsNotNone(len(sync_state.mock_calls)) def test_after_start(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, 'sync_state') as sync_state: bgp_dr.after_start() self.assertIsNotNone(len(sync_state.mock_calls)) def test_agent_updated(self): bgp_dr = bgp_dragent.BgpDrAgentWithStateReport(HOSTNAME) payload = {'admin_state_up': True} with mock.patch.object(bgp_dr, 'agent_updated') as agent_updated: bgp_dr.agent_updated(self.context, payload) self.assertIsNotNone(len(agent_updated.mock_calls)) self.assertEqual(1, bgp_dr.agent_updated.call_count) def _test_sync_state_helper(self, bgp_speaker_list=None, cached_info=None, safe_configure_call_count=0, sync_bgp_speaker_call_count=0, remove_bgp_speaker_call_count=0, remove_bgp_speaker_ids=None, added_bgp_speakers=None, synced_bgp_speakers=None): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) attrs_to_mock = dict( [(a, mock.Mock()) for a in ['plugin_rpc', 'sync_bgp_speaker', 'safe_configure_dragent_for_bgp_speaker', 'remove_bgp_speaker_from_dragent']]) with mock.patch.multiple(bgp_dr, **attrs_to_mock): if not cached_info: cached_info = {} if not added_bgp_speakers: added_bgp_speakers = [] if not remove_bgp_speaker_ids: remove_bgp_speaker_ids = [] if not synced_bgp_speakers: synced_bgp_speakers = [] bgp_dr.plugin_rpc.get_bgp_speakers.return_value = bgp_speaker_list bgp_dr.cache.cache = cached_info bgp_dr.cache.clear_cache = mock.Mock() bgp_dr.sync_state(mock.ANY) self.assertEqual( remove_bgp_speaker_call_count, bgp_dr.remove_bgp_speaker_from_dragent.call_count) if remove_bgp_speaker_call_count: expected_calls = [mock.call(bgp_speaker_id) for bgp_speaker_id in remove_bgp_speaker_ids] bgp_dr.remove_bgp_speaker_from_dragent.assert_has_calls( expected_calls) self.assertEqual( safe_configure_call_count, bgp_dr.safe_configure_dragent_for_bgp_speaker.call_count) if safe_configure_call_count: expected_calls = [mock.call(bgp_speaker) for bgp_speaker in added_bgp_speakers] bgp_dr.safe_configure_dragent_for_bgp_speaker.assert_has_calls( expected_calls) self.assertEqual(sync_bgp_speaker_call_count, bgp_dr.sync_bgp_speaker.call_count) if sync_bgp_speaker_call_count: expected_calls = [mock.call(bgp_speaker) for bgp_speaker in synced_bgp_speakers] bgp_dr.sync_bgp_speaker.assert_has_calls(expected_calls) def test_sync_state_bgp_speaker_added(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}] self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, safe_configure_call_count=1, added_bgp_speakers=bgp_speaker_list) def test_sync_state_bgp_speaker_deleted(self): bgp_speaker_list = [] cached_bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': ['peer-1'], 'advertised_routes': []} cached_info = {'foo-id': cached_bgp_speaker} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, remove_bgp_speaker_call_count=1, remove_bgp_speaker_ids=['foo-id']) def test_sync_state_added_and_deleted(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}] cached_bgp_speaker = {'bgp_speaker': {'local_as': 12345}, 'peers': ['peer-1'], 'advertised_routes': []} cached_info = {'bar-id': cached_bgp_speaker} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, remove_bgp_speaker_call_count=1, remove_bgp_speaker_ids=['bar-id'], safe_configure_call_count=1, added_bgp_speakers=bgp_speaker_list) def test_sync_state_added_and_synced(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}, {'id': 'bar-id', 'peers': ['peer-2'], 'advertised_routes': []}, {'id': 'temp-id', 'peers': ['temp-1'], 'advertised_routes': []}] cached_bgp_speaker = {'id': 'bar-id', 'bgp_speaker': {'id': 'bar-id'}, 'peers': ['peer-1'], 'advertised_routes': []} cached_bgp_speaker_2 = {'id': 'temp-id', 'bgp_speaker': {'id': 'temp-id'}, 'peers': ['temp-1'], 'advertised_routes': []} cached_info = {'bar-id': cached_bgp_speaker, 'temp-id': cached_bgp_speaker_2} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, safe_configure_call_count=1, added_bgp_speakers=[bgp_speaker_list[0]], sync_bgp_speaker_call_count=2, synced_bgp_speakers=[bgp_speaker_list[1], bgp_speaker_list[2]] ) def test_sync_state_added_synced_and_removed(self): bgp_speaker_list = [{'id': 'foo-id', 'local_as': 12345, 'peers': [], 'advertised_routes': []}, {'id': 'bar-id', 'peers': ['peer-2'], 'advertised_routes': []}] cached_bgp_speaker = {'id': 'bar-id', 'bgp_speaker': {'id': 'bar-id'}, 'peers': ['peer-1'], 'advertised_routes': []} cached_bgp_speaker_2 = {'id': 'temp-id', 'bgp_speaker': {'id': 'temp-id'}, 'peers': ['temp-1'], 'advertised_routes': []} cached_info = {'bar-id': cached_bgp_speaker, 'temp-id': cached_bgp_speaker_2} self._test_sync_state_helper(bgp_speaker_list=bgp_speaker_list, cached_info=cached_info, remove_bgp_speaker_call_count=1, remove_bgp_speaker_ids=['temp-id'], safe_configure_call_count=1, added_bgp_speakers=[bgp_speaker_list[0]], sync_bgp_speaker_call_count=1, synced_bgp_speakers=[bgp_speaker_list[1]]) def _test_sync_bgp_speaker_helper(self, bgp_speaker, cached_info=None, remove_bgp_peer_call_count=0, removed_bgp_peer_ip_list=None, withdraw_route_call_count=0, withdraw_routes_list=None, add_bgp_peers_called=False, advertise_routes_called=False): if not cached_info: cached_info = {} if not removed_bgp_peer_ip_list: removed_bgp_peer_ip_list = [] if not withdraw_routes_list: withdraw_routes_list = [] bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) attrs_to_mock = dict( [(a, mock.Mock()) for a in ['remove_bgp_peer_from_bgp_speaker', 'add_bgp_peers_to_bgp_speaker', 'advertise_routes_via_bgp_speaker', 'withdraw_route_via_bgp_speaker']]) with mock.patch.multiple(bgp_dr, **attrs_to_mock): bgp_dr.cache.cache = cached_info bgp_dr.sync_bgp_speaker(bgp_speaker) self.assertEqual( remove_bgp_peer_call_count, bgp_dr.remove_bgp_peer_from_bgp_speaker.call_count) if remove_bgp_peer_call_count: expected_calls = [mock.call(bgp_speaker['id'], peer_ip) for peer_ip in removed_bgp_peer_ip_list] bgp_dr.remove_bgp_peer_from_bgp_speaker.assert_has_calls( expected_calls) self.assertEqual(add_bgp_peers_called, bgp_dr.add_bgp_peers_to_bgp_speaker.called) if add_bgp_peers_called: bgp_dr.add_bgp_peers_to_bgp_speaker.assert_called_with( bgp_speaker) self.assertEqual( withdraw_route_call_count, bgp_dr.withdraw_route_via_bgp_speaker.call_count) if withdraw_route_call_count: expected_calls = [mock.call(bgp_speaker['id'], 12345, route) for route in withdraw_routes_list] bgp_dr.withdraw_route_via_bgp_speaker.assert_has_calls( expected_calls) self.assertEqual(advertise_routes_called, bgp_dr.advertise_routes_via_bgp_speaker.called) if advertise_routes_called: bgp_dr.advertise_routes_via_bgp_speaker.assert_called_with( bgp_speaker) def test_sync_bgp_speaker_bgp_peers_updated(self): peers = [{'id': 'peer-1', 'peer_ip': '1.1.1.1'}, {'id': 'peer-2', 'peer_ip': '2.2.2.2'}] bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': peers, 'advertised_routes': []} cached_peers = {'1.1.1.1': {'id': 'peer-2', 'peer_ip': '1.1.1.1'}, '3.3.3.3': {'id': 'peer-3', 'peer_ip': '3.3.3.3'}} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': cached_peers, 'advertised_routes': []}} self._test_sync_bgp_speaker_helper( bgp_speaker, cached_info=cached_bgp_speaker, remove_bgp_peer_call_count=1, removed_bgp_peer_ip_list=['3.3.3.3'], add_bgp_peers_called=True, advertise_routes_called=False) def test_sync_bgp_speaker_routes_updated(self): adv_routes = [{'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'}, {'destination': '20.0.0.0/24', 'next_hop': '2.2.2.2'}] bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': {}, 'advertised_routes': adv_routes} cached_adv_routes = [{'destination': '20.0.0.0/24', 'next_hop': '2.2.2.2'}, {'destination': '30.0.0.0/24', 'next_hop': '3.3.3.3'}] cached_bgp_speaker = { 'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': cached_adv_routes}} self._test_sync_bgp_speaker_helper( bgp_speaker, cached_info=cached_bgp_speaker, withdraw_route_call_count=1, withdraw_routes_list=[cached_adv_routes[1]], add_bgp_peers_called=False, advertise_routes_called=True) def test_sync_bgp_speaker_peers_routes_added(self): peers = [{'id': 'peer-1', 'peer_ip': '1.1.1.1'}, {'id': 'peer-2', 'peer_ip': '2.2.2.2'}] adv_routes = [{'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'}, {'destination': '20.0.0.0/24', 'next_hop': '2.2.2.2'}] bgp_speaker = {'id': 'foo-id', 'local_as': 12345, 'peers': peers, 'advertised_routes': adv_routes} cached_bgp_speaker = { 'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': []}} self._test_sync_bgp_speaker_helper( bgp_speaker, cached_info=cached_bgp_speaker, add_bgp_peers_called=True, advertise_routes_called=True) def test_sync_state_plugin_error(self): with mock.patch(BGP_PLUGIN) as plug: mock_plugin = mock.Mock() mock_plugin.get_bgp_speakers.side_effect = Exception plug.return_value = mock_plugin with mock.patch.object(bgp_dragent.LOG, 'error') as log: bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, 'schedule_full_resync') as schedule_full_resync: bgp_dr.sync_state(mock.ANY) self.assertTrue(log.called) self.assertTrue(schedule_full_resync.called) def test_periodic_resync(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) with mock.patch.object(bgp_dr, '_periodic_resync_helper') as resync_helper: bgp_dr.periodic_resync(self.context) self.assertTrue(resync_helper.called) def test_periodic_resync_helper(self): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) bgp_dr.schedule_resync('foo reason', 'foo-id') with mock.patch.object(bgp_dr, 'sync_state') as sync_state: sync_state.side_effect = RuntimeError with testtools.ExpectedException(RuntimeError): bgp_dr._periodic_resync_helper(self.context) self.assertTrue(sync_state.called) self.assertEqual(len(bgp_dr.needs_resync_reasons), 0) def _test_add_bgp_peer_helper(self, bgp_speaker_id, bgp_peer, cached_bgp_speaker, put_bgp_peer_called=True): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) bgp_dr.cache.cache = cached_bgp_speaker with mock.patch.object( bgp_dr.cache, 'put_bgp_peer') as mock_put_bgp_peer: bgp_dr.add_bgp_peer_to_bgp_speaker('foo-id', 12345, bgp_peer) if put_bgp_peer_called: mock_put_bgp_peer.assert_called_once_with( bgp_speaker_id, bgp_peer) else: self.assertFalse(mock_put_bgp_peer.called) def test_add_bgp_peer_not_cached(self): bgp_peer = {'peer_ip': '1.1.1.1', 'remote_as': 34567, 'auth_type': 'md5', 'password': 'abc'} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': []}} self._test_add_bgp_peer_helper('foo-id', bgp_peer, cached_bgp_speaker) def test_add_bgp_peer_already_cached(self): bgp_peer = {'peer_ip': '1.1.1.1', 'remote_as': 34567, 'auth_type': 'md5', 'password': 'abc'} cached_peers = {'1.1.1.1': {'peer_ip': '1.1.1.1', 'remote_as': 34567}} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': cached_peers, 'advertised_routes': []}} self._test_add_bgp_peer_helper('foo-id', bgp_peer, cached_bgp_speaker, put_bgp_peer_called=False) def _test_advertise_route_helper(self, bgp_speaker_id, route, cached_bgp_speaker, put_adv_route_called=True): bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) bgp_dr.cache.cache = cached_bgp_speaker with mock.patch.object( bgp_dr.cache, 'put_adv_route') as mock_put_adv_route: bgp_dr.advertise_route_via_bgp_speaker(bgp_speaker_id, 12345, route) if put_adv_route_called: mock_put_adv_route.assert_called_once_with( bgp_speaker_id, route) else: self.assertFalse(mock_put_adv_route.called) def test_advertise_route_helper_not_cached(self): route = {'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': []}} self._test_advertise_route_helper('foo-id', route, cached_bgp_speaker, put_adv_route_called=True) def test_advertise_route_helper_already_cached(self): route = {'destination': '10.0.0.0/24', 'next_hop': '1.1.1.1'} cached_bgp_speaker = {'foo-id': {'bgp_speaker': {'local_as': 12345}, 'peers': {}, 'advertised_routes': [route]}} self._test_advertise_route_helper('foo-id', route, cached_bgp_speaker, put_adv_route_called=False) class TestBgpDrAgentEventHandler(base.BaseTestCase): cache_cls = 'neutron_dynamic_routing.services.bgp.'\ 'agent.bgp_dragent.BgpSpeakerCache' def setUp(self): super(TestBgpDrAgentEventHandler, self).setUp() cfg.CONF.register_opts(bgp_config.BGP_DRIVER_OPTS, 'BGP') cfg.CONF.register_opts(bgp_config.BGP_PROTO_CONFIG_OPTS, 'BGP') mock_log_p = mock.patch.object(bgp_dragent, 'LOG') self.mock_log = mock_log_p.start() self.plugin_p = mock.patch(BGP_PLUGIN) plugin_cls = self.plugin_p.start() self.plugin = mock.Mock() plugin_cls.return_value = self.plugin self.cache_p = mock.patch(self.cache_cls) cache_cls = self.cache_p.start() self.cache = mock.Mock() cache_cls.return_value = self.cache self.driver_cls_p = mock.patch( 'neutron_dynamic_routing.services.bgp.agent.bgp_dragent.' 'importutils.import_class') self.driver_cls = self.driver_cls_p.start() self.bgp_dr = bgp_dragent.BgpDrAgent(HOSTNAME) self.schedule_full_resync_p = mock.patch.object( self.bgp_dr, 'schedule_full_resync') self.schedule_full_resync = self.schedule_full_resync_p.start() self.context = mock.Mock() def test_bgp_speaker_create_end(self): payload = {'bgp_speaker': {'id': FAKE_BGPSPEAKER_UUID}} with mock.patch.object(self.bgp_dr, 'add_bgp_speaker_helper') as enable: self.bgp_dr.bgp_speaker_create_end(None, payload) enable.assert_called_once_with(FAKE_BGP_SPEAKER['id']) def test_bgp_peer_association_end(self): payload = {'bgp_peer': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'peer_id': FAKE_BGPPEER_UUID}} with mock.patch.object(self.bgp_dr, 'add_bgp_peer_helper') as enable: self.bgp_dr.bgp_peer_association_end(None, payload) enable.assert_called_once_with(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER['id']) def test_route_advertisement_end(self): routes = [{'destination': '2.2.2.2/32', 'next_hop': '3.3.3.3'}, {'destination': '4.4.4.4/32', 'next_hop': '5.5.5.5'}] payload = {'advertise_routes': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'routes': routes}} expected_calls = [mock.call(FAKE_BGP_SPEAKER['id'], routes)] with mock.patch.object(self.bgp_dr, 'add_routes_helper') as enable: self.bgp_dr.bgp_routes_advertisement_end(None, payload) enable.assert_has_calls(expected_calls) def test_add_bgp_speaker_helper(self): self.plugin.get_bgp_speaker_info.return_value = FAKE_BGP_SPEAKER add_bs_p = mock.patch.object(self.bgp_dr, 'add_bgp_speaker_on_dragent') add_bs = add_bs_p.start() self.bgp_dr.add_bgp_speaker_helper(FAKE_BGP_SPEAKER['id']) self.plugin.assert_has_calls([ mock.call.get_bgp_speaker_info(mock.ANY, FAKE_BGP_SPEAKER['id'])]) add_bs.assert_called_once_with(FAKE_BGP_SPEAKER) def test_add_bgp_peer_helper(self): self.plugin.get_bgp_peer_info.return_value = FAKE_BGP_PEER add_bp_p = mock.patch.object(self.bgp_dr, 'add_bgp_peer_to_bgp_speaker') add_bp = add_bp_p.start() self.bgp_dr.add_bgp_peer_helper(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER['id']) self.plugin.assert_has_calls([ mock.call.get_bgp_peer_info(mock.ANY, FAKE_BGP_PEER['id'])]) self.assertEqual(1, add_bp.call_count) def test_add_routes_helper(self): add_rt_p = mock.patch.object(self.bgp_dr, 'advertise_route_via_bgp_speaker') add_bp = add_rt_p.start() self.bgp_dr.add_routes_helper(FAKE_BGP_SPEAKER['id'], FAKE_ROUTES) self.assertEqual(1, add_bp.call_count) def test_bgp_speaker_remove_end(self): payload = {'bgp_speaker': {'id': FAKE_BGPSPEAKER_UUID}} with mock.patch.object(self.bgp_dr, 'remove_bgp_speaker_from_dragent') as disable: self.bgp_dr.bgp_speaker_remove_end(None, payload) disable.assert_called_once_with(FAKE_BGP_SPEAKER['id']) def test_bgp_peer_disassociation_end(self): payload = {'bgp_peer': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'peer_ip': '1.1.1.1'}} with mock.patch.object(self.bgp_dr, 'remove_bgp_peer_from_bgp_speaker') as disable: self.bgp_dr.bgp_peer_disassociation_end(None, payload) disable.assert_called_once_with(FAKE_BGPSPEAKER_UUID, FAKE_BGP_PEER['peer_ip']) def test_bgp_routes_withdrawal_end(self): withdraw_routes = [{'destination': '2.2.2.2/32'}, {'destination': '3.3.3.3/32'}] payload = {'withdraw_routes': {'speaker_id': FAKE_BGPSPEAKER_UUID, 'routes': withdraw_routes}} expected_calls = [mock.call(FAKE_BGP_SPEAKER['id'], withdraw_routes)] with mock.patch.object(self.bgp_dr, 'withdraw_routes_helper') as disable: self.bgp_dr.bgp_routes_withdrawal_end(None, payload) disable.assert_has_calls(expected_calls) class TestBGPSpeakerCache(base.BaseTestCase): def setUp(self): super(TestBGPSpeakerCache, self).setUp() self.expected_cache = {FAKE_BGP_SPEAKER['id']: {'bgp_speaker': FAKE_BGP_SPEAKER, 'peers': {}, 'advertised_routes': []}} self.bs_cache = bgp_dragent.BgpSpeakerCache() def test_put_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_put_bgp_speaker_existing(self): prev_bs_info = {'id': 'foo-id'} with mock.patch.object(self.bs_cache, 'remove_bgp_speaker_by_id') as remove: self.bs_cache.cache[FAKE_BGP_SPEAKER['id']] = prev_bs_info self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) remove.assert_called_once_with(prev_bs_info) self.assertEqual(self.expected_cache, self.bs_cache.cache) def remove_bgp_speaker_by_id(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual(1, len(self.bs_cache.cache)) self.bs_cache.remove_bgp_speaker_by_id(FAKE_BGP_SPEAKER['id']) self.assertEqual(0, len(self.bs_cache.cache)) def test_get_bgp_speaker_by_id(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual( FAKE_BGP_SPEAKER, self.bs_cache.get_bgp_speaker_by_id(FAKE_BGP_SPEAKER['id'])) def test_get_bgp_speaker_ids(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.assertEqual([FAKE_BGP_SPEAKER['id']], list(self.bs_cache.get_bgp_speaker_ids())) def _test_bgp_peer_helper(self, remove=False): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.bs_cache.put_bgp_peer(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER) expected_cache = copy.deepcopy(self.expected_cache) expected_cache[FAKE_BGP_SPEAKER['id']]['peers'] = { FAKE_BGP_PEER['peer_ip']: FAKE_BGP_PEER} self.assertEqual(expected_cache, self.bs_cache.cache) if remove: self.bs_cache.remove_bgp_peer_by_ip(FAKE_BGP_SPEAKER['id'], 'foo-ip') self.assertEqual(expected_cache, self.bs_cache.cache) self.bs_cache.remove_bgp_peer_by_ip(FAKE_BGP_SPEAKER['id'], FAKE_BGP_PEER['peer_ip']) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_put_bgp_peer(self): self._test_bgp_peer_helper() def test_remove_bgp_peer(self): self._test_bgp_peer_helper(remove=True) def _test_bgp_speaker_adv_route_helper(self, remove=False): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.bs_cache.put_adv_route(FAKE_BGP_SPEAKER['id'], FAKE_ROUTE) expected_cache = copy.deepcopy(self.expected_cache) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'].append( FAKE_ROUTE) self.assertEqual(expected_cache, self.bs_cache.cache) fake_route_2 = copy.deepcopy(FAKE_ROUTE) fake_route_2['destination'] = '4.4.4.4/32' self.bs_cache.put_adv_route(FAKE_BGP_SPEAKER['id'], fake_route_2) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'].append( fake_route_2) self.assertEqual(expected_cache, self.bs_cache.cache) if remove: self.bs_cache.remove_adv_route(FAKE_BGP_SPEAKER['id'], fake_route_2) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'] = ( [FAKE_ROUTE]) self.assertEqual(expected_cache, self.bs_cache.cache) self.bs_cache.remove_adv_route(FAKE_BGP_SPEAKER['id'], FAKE_ROUTE) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_put_bgp_speaker_adv_route(self): self._test_bgp_speaker_adv_route_helper() def test_remove_bgp_speaker_adv_route(self): self._test_bgp_speaker_adv_route_helper(remove=True) def test_is_bgp_speaker_adv_route_present(self): self._test_bgp_speaker_adv_route_helper() self.assertTrue(self.bs_cache.is_route_advertised( FAKE_BGP_SPEAKER['id'], FAKE_ROUTE)) self.assertFalse(self.bs_cache.is_route_advertised( FAKE_BGP_SPEAKER['id'], {'destination': 'foo-destination', 'next_hop': 'foo-next-hop'})) def test_put_bgp_speaker_adv_route_and_remove_with_next_hop_none(self): self.bs_cache.put_bgp_speaker(FAKE_BGP_SPEAKER) self.bs_cache.put_adv_route(FAKE_BGP_SPEAKER['id'], FAKE_ROUTE) expected_cache = copy.deepcopy(self.expected_cache) expected_cache[FAKE_BGP_SPEAKER['id']]['advertised_routes'].append( FAKE_ROUTE) self.assertEqual(expected_cache, self.bs_cache.cache) fake_route_2 = copy.deepcopy(FAKE_ROUTE) fake_route_2.update({'next_hop': None}) self.assertTrue(self.bs_cache.is_route_advertised( FAKE_BGP_SPEAKER['id'], fake_route_2)) self.bs_cache.remove_adv_route(FAKE_BGP_SPEAKER['id'], fake_route_2) self.assertEqual(self.expected_cache, self.bs_cache.cache) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/0000775000175000017500000000000000000000000031371 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/__init__.py0000664000175000017500000000000000000000000033470 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/0000775000175000017500000000000000000000000032647 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/__init__.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/__init_0000664000175000017500000000000000000000000034160 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/test_driver.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/test_dr0000664000175000017500000003554400000000000034251 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from unittest import mock from neutron.tests import base from os_ken.services.protocols.bgp import bgpspeaker from os_ken.services.protocols.bgp.rtconf.neighbors import CONNECT_MODE_ACTIVE from oslo_config import cfg from oslo_utils import encodeutils from neutron_dynamic_routing.services.bgp.agent import config as bgp_config from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.agent.driver.os_ken import driver as os_ken_driver # noqa # Test variables for BGP Speaker FAKE_LOCAL_AS1 = 12345 FAKE_LOCAL_AS2 = 23456 FAKE_ROUTER_ID = '1.1.1.1' # Test variables for BGP Peer FAKE_PEER_AS = 45678 FAKE_PEER_IP = '2.2.2.5' FAKE_PEER_IPV6 = '2001:db8::' FAKE_AUTH_TYPE = 'md5' FAKE_PEER_PASSWORD = 'awesome' # Test variables for Route FAKE_ROUTE = '2.2.2.0/24' FAKE_NEXTHOP = '5.5.5.5' class TestOsKenBgpDriver(base.BaseTestCase): def setUp(self): super(TestOsKenBgpDriver, self).setUp() cfg.CONF.register_opts(bgp_config.BGP_PROTO_CONFIG_OPTS, 'BGP') cfg.CONF.set_override('bgp_router_id', FAKE_ROUTER_ID, 'BGP') self.os_ken_bgp_driver = os_ken_driver.OsKenBgpDriver(cfg.CONF.BGP) mock_os_ken_speaker_p = mock.patch.object(bgpspeaker, 'BGPSpeaker') self.mock_os_ken_speaker = mock_os_ken_speaker_p.start() def test_add_new_bgp_speaker(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.mock_os_ken_speaker.assert_called_once_with( as_number=FAKE_LOCAL_AS1, router_id=FAKE_ROUTER_ID, bgp_server_port=0, best_path_change_handler=os_ken_driver.best_path_change_cb, peer_down_handler=os_ken_driver.bgp_peer_down_cb, peer_up_handler=os_ken_driver.bgp_peer_up_cb) def test_remove_bgp_speaker(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) self.os_ken_bgp_driver.delete_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(0, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.assertEqual(1, speaker.shutdown.call_count) def test_add_bgp_peer_without_password(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IP, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=None, connect_mode=CONNECT_MODE_ACTIVE) def test_add_bgp_peer_with_password(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, FAKE_AUTH_TYPE, FAKE_PEER_PASSWORD) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IP, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=encodeutils.to_utf8(FAKE_PEER_PASSWORD), connect_mode=CONNECT_MODE_ACTIVE) def test_add_bgp_peer_with_unicode_password(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) NEW_FAKE_PEER_PASSWORD = str(FAKE_PEER_PASSWORD) self.os_ken_bgp_driver.add_bgp_peer( FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, FAKE_AUTH_TYPE, NEW_FAKE_PEER_PASSWORD) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IP, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=encodeutils.to_utf8(NEW_FAKE_PEER_PASSWORD), connect_mode=CONNECT_MODE_ACTIVE) def test_add_bgp_peer_with_ipv6(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IPV6, FAKE_PEER_AS) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_add.assert_called_once_with( address=FAKE_PEER_IPV6, remote_as=FAKE_PEER_AS, enable_ipv4=True, enable_ipv6=True, password=None, connect_mode=CONNECT_MODE_ACTIVE) def test_remove_bgp_peer(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.delete_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.neighbor_del.assert_called_once_with(address=FAKE_PEER_IP) def test_advertise_route(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.advertise_route(FAKE_LOCAL_AS1, FAKE_ROUTE, FAKE_NEXTHOP) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.prefix_add.assert_called_once_with(prefix=FAKE_ROUTE, next_hop=FAKE_NEXTHOP) def test_withdraw_route(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.withdraw_route(FAKE_LOCAL_AS1, FAKE_ROUTE) speaker = self.os_ken_bgp_driver.cache.get_bgp_speaker(FAKE_LOCAL_AS1) speaker.prefix_del.assert_called_once_with(prefix=FAKE_ROUTE) def test_add_same_bgp_speakers_twice(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.BgpSpeakerAlreadyScheduled, self.os_ken_bgp_driver.add_bgp_speaker, FAKE_LOCAL_AS1) def test_add_different_bgp_speakers_when_one_already_added(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.BgpSpeakerMaxScheduled, self.os_ken_bgp_driver.add_bgp_speaker, FAKE_LOCAL_AS2) def test_add_bgp_speaker_with_invalid_asnum_paramtype(self): self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_speaker, '12345') def test_add_bgp_speaker_with_invalid_asnum_range(self): self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_speaker, -1) self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_speaker, 4294967296) # valid when enables 4 byte AS number self.os_ken_bgp_driver.add_bgp_speaker(65536) def test_add_bgp_peer_with_invalid_paramtype(self): # Test with an invalid asnum data-type self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, '12345') # Test with an invalid auth-type and an invalid password self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'sha-1', 1234) # Test with an invalid auth-type and a valid password self.assertRaises(bgp_driver_exc.InvaildAuthType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'hmac-md5', FAKE_PEER_PASSWORD) # Test with none auth-type and a valid password self.assertRaises(bgp_driver_exc.InvaildAuthType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'none', FAKE_PEER_PASSWORD) # Test with none auth-type and an invalid password self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, 'none', 1234) # Test with a valid auth-type and no password self.assertRaises(bgp_driver_exc.PasswordNotSpecified, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS, FAKE_AUTH_TYPE, None) # Test with a invalid ip address self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, '1.2.3.a', FAKE_PEER_AS, FAKE_AUTH_TYPE, FAKE_PEER_PASSWORD) def test_add_bgp_peer_with_invalid_asnum_range(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, -1) self.assertRaises(bgp_driver_exc.InvalidParamRange, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, 4294967296) # valid when enables 4 byte AS number self.os_ken_bgp_driver.add_bgp_peer(FAKE_LOCAL_AS1, FAKE_PEER_IP, 65536) def test_add_bgp_peer_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.add_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP, FAKE_PEER_AS) def test_remove_bgp_peer_with_invalid_paramtype(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.delete_bgp_peer, FAKE_LOCAL_AS1, 12345) def test_remove_bgp_peer_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.delete_bgp_peer, FAKE_LOCAL_AS1, FAKE_PEER_IP) def test_advertise_route_with_invalid_paramtype(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.advertise_route, FAKE_LOCAL_AS1, 12345, FAKE_NEXTHOP) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.advertise_route, FAKE_LOCAL_AS1, FAKE_ROUTE, 12345) def test_advertise_route_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.advertise_route, FAKE_LOCAL_AS1, FAKE_ROUTE, FAKE_NEXTHOP) def test_withdraw_route_with_invalid_paramtype(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.withdraw_route, FAKE_LOCAL_AS1, 12345) self.assertRaises(bgp_driver_exc.InvalidParamType, self.os_ken_bgp_driver.withdraw_route, FAKE_LOCAL_AS1, 12345) def test_withdraw_route_without_adding_speaker(self): self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.withdraw_route, FAKE_LOCAL_AS1, FAKE_ROUTE) def test_add_multiple_bgp_speakers(self): self.os_ken_bgp_driver.add_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.assertRaises(bgp_driver_exc.BgpSpeakerMaxScheduled, self.os_ken_bgp_driver.add_bgp_speaker, FAKE_LOCAL_AS2) self.assertRaises(bgp_driver_exc.BgpSpeakerNotAdded, self.os_ken_bgp_driver.delete_bgp_speaker, FAKE_LOCAL_AS2) self.assertEqual(1, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) self.os_ken_bgp_driver.delete_bgp_speaker(FAKE_LOCAL_AS1) self.assertEqual(0, self.os_ken_bgp_driver.cache.get_hosted_bgp_speakers_count()) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/driver/test_utils.py0000664000175000017500000001713200000000000034146 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. from neutron_lib import constants as lib_consts from neutron.tests import base from neutron_dynamic_routing.services.bgp.agent.driver import exceptions as bgp_driver_exc # noqa from neutron_dynamic_routing.services.bgp.agent.driver import utils as bgp_driver_utils # noqa from neutron_dynamic_routing.services.bgp.common import constants as bgp_consts # noqa FAKE_IP = '2.2.2.5' FAKE_IPV6 = '2001:db8::' FAKE_LOCAL_AS = 12345 FAKE_OS_KEN_SPEAKER = {} EXC_INV_PARAMTYPE = "Parameter %(param)s must be of %(param_type)s type." EXC_INV_PARAMRANGE = "%(param)s must be in %(range)s range." EXC_PASSWORD_NOTSPEC = "Password not specified for authentication " + \ "type=%(auth_type)s." EXC_INV_AUTHTYPE = "Authentication type not supported. Requested " + \ "type=%(auth_type)s." class TestValidateMethod(base.BaseTestCase): def setUp(self): super(TestValidateMethod, self).setUp() def test_validate_as_num_with_valid_as_num(self): self.assertIsNone(bgp_driver_utils.validate_as_num('local_as', 64512)) def test_validate_as_num_with_string_as_num(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': 'local_as', 'param_type': 'integer'}): bgp_driver_utils.validate_as_num('local_as', '64512') def test_validate_as_num_with_invalid_max_range(self): allowed_range = ('\\[' + str(bgp_consts.MIN_ASNUM) + '-' + str(bgp_consts.MAX_4BYTE_ASNUM) + '\\]') with self.assertRaisesRegex( bgp_driver_exc.InvalidParamRange, EXC_INV_PARAMRANGE % {'param': 'local_as', 'range': allowed_range}): bgp_driver_utils.validate_as_num('local_as', bgp_consts.MAX_4BYTE_ASNUM + 1) def test_validate_as_num_with_invalid_min_range(self): allowed_range = ('\\[' + str(bgp_consts.MIN_ASNUM) + '-' + str(bgp_consts.MAX_4BYTE_ASNUM) + '\\]') with self.assertRaisesRegex( bgp_driver_exc.InvalidParamRange, EXC_INV_PARAMRANGE % {'param': 'local_as', 'range': allowed_range}): bgp_driver_utils.validate_as_num('local_as', 0) def test_validate_auth_with_valid_auth_type(self): passwords = [None, 'password'] for (auth_type, passwd) in zip(bgp_consts.SUPPORTED_AUTH_TYPES, passwords): self.assertIsNone(bgp_driver_utils.validate_auth(auth_type, passwd)) def test_validate_auth_with_integer_password(self): auth_type = bgp_consts.SUPPORTED_AUTH_TYPES[1] with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '12345', 'param_type': 'string'}): bgp_driver_utils.validate_auth(auth_type, 12345) def test_validate_auth_with_invalid_auth_type(self): auth_type = 'abcde' with self.assertRaisesRegex( bgp_driver_exc.InvaildAuthType, EXC_INV_AUTHTYPE % {'auth_type': auth_type}): bgp_driver_utils.validate_auth(auth_type, 'password') def test_validate_auth_with_not_none_auth_type_and_none_password(self): auth_type = bgp_consts.SUPPORTED_AUTH_TYPES[1] with self.assertRaisesRegex( bgp_driver_exc.PasswordNotSpecified, EXC_PASSWORD_NOTSPEC % {'auth_type': auth_type}): bgp_driver_utils.validate_auth(auth_type, None) def test_validate_auth_with_none_auth_type_and_not_none_password(self): auth_type = None with self.assertRaisesRegex( bgp_driver_exc.InvaildAuthType, EXC_INV_AUTHTYPE % {'auth_type': auth_type}): bgp_driver_utils.validate_auth(auth_type, 'password') def test_validate_ip_addr_with_ipv4_address(self): self.assertEqual(lib_consts.IP_VERSION_4, bgp_driver_utils.validate_ip_addr(FAKE_IP)) def test_validate_ip_addr_with_ipv6_address(self): self.assertEqual(lib_consts.IP_VERSION_6, bgp_driver_utils.validate_ip_addr(FAKE_IPV6)) def test_validate_ip_addr_with_integer_ip(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '12345', 'param_type': 'ip-address'}): bgp_driver_utils.validate_ip_addr(12345) def test_validate_ip_addr_with_invalid_ipv4_type(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '1.2.3.a', 'param_type': 'ip-address'}): bgp_driver_utils.validate_ip_addr('1.2.3.a') def test_validate_ip_addr_with_invalid_ipv6_type(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '2001:db8::ggg', 'param_type': 'ip-address'}): bgp_driver_utils.validate_ip_addr('2001:db8::ggg') def test_validate_string_with_string(self): self.assertIsNone(bgp_driver_utils.validate_string(FAKE_IP)) def test_validate_string_with_integer_param(self): with self.assertRaisesRegex( bgp_driver_exc.InvalidParamType, EXC_INV_PARAMTYPE % {'param': '12345', 'param_type': 'string'}): bgp_driver_utils.validate_string(12345) class TestBgpMultiSpeakerCache(base.BaseTestCase): def setUp(self): super(TestBgpMultiSpeakerCache, self).setUp() self.expected_cache = {FAKE_LOCAL_AS: FAKE_OS_KEN_SPEAKER} self.bs_cache = bgp_driver_utils.BgpMultiSpeakerCache() def test_put_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual(self.expected_cache, self.bs_cache.cache) def test_remove_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual(1, len(self.bs_cache.cache)) self.bs_cache.remove_bgp_speaker(FAKE_LOCAL_AS) self.assertEqual(0, len(self.bs_cache.cache)) def test_get_bgp_speaker(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual( FAKE_OS_KEN_SPEAKER, self.bs_cache.get_bgp_speaker(FAKE_LOCAL_AS)) def test_get_hosted_bgp_speakers_count(self): self.bs_cache.put_bgp_speaker(FAKE_LOCAL_AS, FAKE_OS_KEN_SPEAKER) self.assertEqual(1, self.bs_cache.get_hosted_bgp_speakers_count()) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4006605 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/0000775000175000017500000000000000000000000032054 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/__init__.py0000664000175000017500000000000000000000000034153 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022600000000000011455 xustar0000000000000000128 path=neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/test_bgp_dragent_scheduler.py 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/scheduler/test_bgp_dr0000664000175000017500000003514200000000000034300 0ustar00zuulzuul00000000000000# Copyright 2016 Huawei Technologies India Pvt. Ltd. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from neutron.tests.unit import testlib_api from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import context from oslo_utils import importutils import testscenarios from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.db import bgp_dragentscheduler_db as bgp_dras_db from neutron_dynamic_routing.services.bgp import bgp_plugin from neutron_dynamic_routing.services.bgp.scheduler import bgp_dragent_scheduler as bgp_dras # noqa from neutron_dynamic_routing.tests.common import helpers # Required to generate tests from scenarios. Not compatible with nose. load_tests = testscenarios.load_tests_apply_scenarios class TestBgpDrAgentSchedulerBaseTestCase(testlib_api.SqlTestCase): def setUp(self): super(TestBgpDrAgentSchedulerBaseTestCase, self).setUp() self.ctx = context.get_admin_context() self.bgp_speaker = {'id': 'foo_bgp_speaker_id'} self.bgp_speaker_id = 'foo_bgp_speaker_id' self._save_bgp_speaker(self.bgp_speaker_id) def _create_and_set_agents_down(self, hosts, down_agent_count=0, admin_state_up=True): agents = [] for i, host in enumerate(hosts): is_alive = i >= down_agent_count agents.append(helpers.register_bgp_dragent( host, admin_state_up=admin_state_up, alive=is_alive)) return agents def _save_bgp_speaker(self, bgp_speaker_id): cls = bgp_db.BgpDbMixin() bgp_speaker_body = {'bgp_speaker': { 'name': 'fake_bgp_speaker', 'ip_version': '4', 'local_as': '123', 'advertise_floating_ip_host_routes': False, 'advertise_tenant_networks': False, 'peers': [], 'networks': []}} cls._save_bgp_speaker(self.ctx, bgp_speaker_body, uuid=bgp_speaker_id) def _get_dragent_bgp_speaker_bindings(self, bgp_speaker_id): return self.ctx.session.query( bgp_dras_db.BgpSpeakerDrAgentBinding).filter_by( bgp_speaker_id=bgp_speaker_id).all() def _test_schedule_bind_bgp_speaker(self, agents, bgp_speaker_id): scheduler = bgp_dras.ChanceScheduler() scheduler.resource_filter.bind(self.ctx, agents, bgp_speaker_id) results = self._get_dragent_bgp_speaker_bindings(bgp_speaker_id) for result in results: self.assertEqual(bgp_speaker_id, result.bgp_speaker_id) class TestSchedulerCallback(TestBgpDrAgentSchedulerBaseTestCase): def setUp(self): super(TestSchedulerCallback, self).setUp() bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.' 'agentnotifiers.bgp_dr_rpc_agent_api.' 'BgpDrAgentNotifyApi') bgp_notify_p.start() rpc_conn_p = mock.patch('neutron_lib.rpc.Connection') rpc_conn_p.start() self.plugin = bgp_plugin.BgpPlugin() self.scheduler = bgp_dras.ChanceScheduler() def _create_test_payload(self, context='test_ctx'): bgp_speaker = {'id': '11111111-2222-3333-4444-555555555555'} payload = events.DBEventPayload( context, metadata={'plugin': self.plugin}, states=(bgp_speaker,)) return payload def test__register_callbacks(self): with mock.patch.object(registry, 'subscribe') as subscribe: scheduler = bgp_dras.ChanceScheduler() expected_calls = [ mock.call(scheduler.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE), ] self.assertEqual(subscribe.call_args_list, expected_calls) with mock.patch.object(registry, 'subscribe') as subscribe: scheduler = bgp_dras.WeightScheduler() expected_calls = [ mock.call(scheduler.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE), ] self.assertEqual(subscribe.call_args_list, expected_calls) def test_schedule_bgp_speaker_callback_with_valid_event(self): payload = self._create_test_payload() with mock.patch.object(self.plugin, 'schedule_bgp_speaker') as sched_bgp: self.scheduler.schedule_bgp_speaker_callback( dr_resources.BGP_SPEAKER, events.AFTER_CREATE, self.scheduler, payload) sched_bgp.assert_called_once_with(mock.ANY, payload.latest_state) def test_schedule_bgp_speaker_callback_with_invalid_event(self): payload = self._create_test_payload() with mock.patch.object(self.plugin, 'schedule_bgp_speaker') as sched_bgp: self.scheduler.schedule_bgp_speaker_callback( dr_resources.BGP_SPEAKER, events.BEFORE_CREATE, self.scheduler, payload) sched_bgp.assert_not_called() class TestBgpDrAgentScheduler(TestBgpDrAgentSchedulerBaseTestCase, bgp_db.BgpDbMixin): def test_schedule_bind_bgp_speaker_single_agent(self): agents = self._create_and_set_agents_down(['host-a']) self._test_schedule_bind_bgp_speaker(agents, self.bgp_speaker_id) def test_schedule_bind_bgp_speaker_multi_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_bgp_speaker(agents, self.bgp_speaker_id) class TestBgpAgentFilter(TestBgpDrAgentSchedulerBaseTestCase, bgp_db.BgpDbMixin, bgp_dras_db.BgpDrAgentSchedulerDbMixin): def setUp(self): super(TestBgpAgentFilter, self).setUp() self.bgp_drscheduler = importutils.import_object( 'neutron_dynamic_routing.services.bgp.scheduler.' 'bgp_dragent_scheduler.ChanceScheduler' ) self.plugin = self def _test_filter_agents_helper(self, bgp_speaker, expected_filtered_dragent_ids=None, expected_num_agents=1): filtered_agents = ( self.plugin.bgp_drscheduler.resource_filter.filter_agents( self.plugin, self.ctx, bgp_speaker)) self.assertEqual(expected_num_agents, filtered_agents['n_agents']) actual_filtered_dragent_ids = [ agent.id for agent in filtered_agents['hostable_agents']] if expected_filtered_dragent_ids is None: expected_filtered_dragent_ids = [] self.assertEqual(len(expected_filtered_dragent_ids), len(actual_filtered_dragent_ids)) for filtered_agent_id in actual_filtered_dragent_ids: self.assertIn(filtered_agent_id, expected_filtered_dragent_ids) def test_filter_agents_single_agent(self): agents = self._create_and_set_agents_down(['host-a']) expected_filtered_dragent_ids = [agents[0].id] self._test_filter_agents_helper( self.bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids) def test_filter_agents_no_agents(self): expected_filtered_dragent_ids = [] self._test_filter_agents_helper( self.bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids, expected_num_agents=0) def test_filter_agents_two_agents(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) expected_filtered_dragent_ids = [agent.id for agent in agents] self._test_filter_agents_helper( self.bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids) def test_filter_agents_agent_already_scheduled(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._test_filter_agents_helper(self.bgp_speaker, expected_num_agents=0) def test_filter_agents_multiple_agents_bgp_speakers(self): agents = self._create_and_set_agents_down(['host-a', 'host-b']) self._test_schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) bgp_speaker = {'id': 'bar-speaker-id'} self._save_bgp_speaker(bgp_speaker['id']) expected_filtered_dragent_ids = [agents[1].id] self._test_filter_agents_helper( bgp_speaker, expected_filtered_dragent_ids=expected_filtered_dragent_ids) class TestAutoScheduleBgpSpeakers(TestBgpDrAgentSchedulerBaseTestCase): """Unit test scenarios for schedule_unscheduled_bgp_speakers. bgp_speaker_present BGP speaker is present or not scheduled_already BGP speaker is already scheduled to the agent or not agent_down BGP DRAgent is down or alive valid_host If true, then an valid host is passed to schedule BGP speaker, else an invalid host is passed. """ scenarios = [ ('BGP speaker present', dict(bgp_speaker_present=True, scheduled_already=False, agent_down=False, valid_host=True, expected_result=True)), ('No BGP speaker', dict(bgp_speaker_present=False, scheduled_already=False, agent_down=False, valid_host=True, expected_result=False)), ('BGP speaker already scheduled', dict(bgp_speaker_present=True, scheduled_already=True, agent_down=False, valid_host=True, expected_result=False)), ('BGP DR agent down', dict(bgp_speaker_present=True, scheduled_already=False, agent_down=True, valid_host=False, expected_result=False)), ('Invalid host', dict(bgp_speaker_present=True, scheduled_already=False, agent_down=False, valid_host=False, expected_result=False)), ] def test_auto_schedule_bgp_speaker(self): scheduler = bgp_dras.ChanceScheduler() if self.bgp_speaker_present: down_agent_count = 1 if self.agent_down else 0 agents = self._create_and_set_agents_down( ['host-a'], down_agent_count=down_agent_count) if self.scheduled_already: self._test_schedule_bind_bgp_speaker(agents, self.bgp_speaker_id) expected_hosted_agents = (1 if self.bgp_speaker_present and self.valid_host else 0) host = "host-a" if self.valid_host else "host-b" observed_ret_value = scheduler.schedule_unscheduled_bgp_speakers( self.ctx, host) self.assertEqual(self.expected_result, observed_ret_value) hosted_agents = self.ctx.session.query( bgp_dras_db.BgpSpeakerDrAgentBinding).all() self.assertEqual(expected_hosted_agents, len(hosted_agents)) class TestRescheduleBgpSpeaker(TestBgpDrAgentSchedulerBaseTestCase, bgp_db.BgpDbMixin): def setUp(self): super(TestRescheduleBgpSpeaker, self).setUp() bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.' 'agentnotifiers.bgp_dr_rpc_agent_api.' 'BgpDrAgentNotifyApi') bgp_notify_p.start() rpc_conn_p = mock.patch('neutron_lib.rpc.Connection') rpc_conn_p.start() self.plugin = bgp_plugin.BgpPlugin() self.scheduler = bgp_dras.ChanceScheduler() self.host1 = 'host-a' self.host2 = 'host-b' def _kill_bgp_dragent(self, hosts): agents = [] for host in hosts: agents.append( helpers.register_bgp_dragent(host=host, alive=False)) return agents def _schedule_bind_bgp_speaker(self, agents, bgp_speaker_id): self.scheduler.resource_filter.bind(self.ctx, agents, bgp_speaker_id) return self._get_dragent_bgp_speaker_bindings(bgp_speaker_id) def test_reschedule_bgp_speaker_bound_to_down_dragent(self): agents = self._create_and_set_agents_down([self.host1, self.host2]) self._schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._kill_bgp_dragent([self.host1]) self.plugin.remove_bgp_speaker_from_down_dragents() binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds[0].agent_id, agents[1].id) def test_no_schedule_with_non_available_dragent(self): agents = self._create_and_set_agents_down([self.host1, self.host2]) self._schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._kill_bgp_dragent([self.host1, self.host2]) self.plugin.remove_bgp_speaker_from_down_dragents() binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds, []) def test_schedule_unbind_bgp_speaker(self): agents = self._create_and_set_agents_down([self.host1, self.host2]) self._schedule_bind_bgp_speaker([agents[0]], self.bgp_speaker_id) self._kill_bgp_dragent([self.host1, self.host2]) self.plugin.remove_bgp_speaker_from_down_dragents() binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds, []) # schedule a unbind bgp speaker agents = self._create_and_set_agents_down([self.host1]) self.scheduler.schedule_all_unscheduled_bgp_speakers(self.ctx) binds = self._get_dragent_bgp_speaker_bindings(self.bgp_speaker_id) self.assertEqual(binds[0].agent_id, agents[0].id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/tests/unit/services/bgp/test_bgp_plugin.py0000664000175000017500000001472300000000000033644 0ustar00zuulzuul00000000000000# Copyright (C) 2017 VA Linux Systems Japan K.K. # Copyright (C) 2017 Fumihiko Kakuma # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock import netaddr from neutron.tests import base from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as n_const from neutron_dynamic_routing.api.rpc.callbacks import resources as dr_resources from neutron_dynamic_routing.db import bgp_db from neutron_dynamic_routing.services.bgp import bgp_plugin class TestBgpPlugin(base.BaseTestCase): def setUp(self): super(TestBgpPlugin, self).setUp() bgp_notify_p = mock.patch('neutron_dynamic_routing.api.rpc.' 'agentnotifiers.bgp_dr_rpc_agent_api.' 'BgpDrAgentNotifyApi') bgp_notify_p.start() rpc_conn_p = mock.patch('neutron_lib.rpc.Connection') rpc_conn_p.start() admin_ctx_p = mock.patch('neutron_lib.context.get_admin_context') self.admin_ctx_m = admin_ctx_p.start() self.fake_admin_ctx = mock.Mock() self.admin_ctx_m.return_value = self.fake_admin_ctx self.plugin = bgp_plugin.BgpPlugin() def _create_test_payload(self, context='test_ctx'): bgp_speaker = {'id': '11111111-2222-3333-4444-555555555555'} payload = events.DBEventPayload( context, metadata={'plugin': self.plugin}, states=(bgp_speaker,)) return payload def test__register_callbacks(self): with mock.patch.object(registry, 'subscribe') as subscribe: plugin = bgp_plugin.BgpPlugin() expected_calls = [ mock.call(plugin.bgp_drscheduler.schedule_bgp_speaker_callback, dr_resources.BGP_SPEAKER, events.AFTER_CREATE), mock.call(plugin.floatingip_update_callback, resources.FLOATING_IP, events.AFTER_CREATE), mock.call(plugin.floatingip_update_callback, resources.FLOATING_IP, events.AFTER_UPDATE), mock.call(plugin.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_CREATE), mock.call(plugin.router_interface_callback, resources.ROUTER_INTERFACE, events.BEFORE_CREATE), mock.call(plugin.router_interface_callback, resources.ROUTER_INTERFACE, events.AFTER_DELETE), mock.call(plugin.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_CREATE), mock.call(plugin.router_gateway_callback, resources.ROUTER_GATEWAY, events.AFTER_DELETE), mock.call(plugin.port_callback, resources.PORT, events.AFTER_UPDATE), ] self.assertEqual(subscribe.call_args_list, expected_calls) def test_create_bgp_speaker(self): test_context = 'create_bgp_context' test_bgp_speaker = {'id': None} payload = self._create_test_payload(context=test_context) with mock.patch.object(bgp_db.BgpDbMixin, 'create_bgp_speaker') as create_bgp_sp: with mock.patch.object(registry, 'publish') as publish: create_bgp_sp.return_value = payload.latest_state self.assertEqual(self.plugin.create_bgp_speaker( test_context, test_bgp_speaker), payload.latest_state) create_bgp_sp.assert_called_once_with(test_context, test_bgp_speaker) publish.assert_called_once_with(dr_resources.BGP_SPEAKER, events.AFTER_CREATE, self.plugin, payload=mock.ANY) publish_payload = publish.call_args_list[0][1]['payload'] self.assertEqual(payload.latest_state, publish_payload.latest_state) self.assertEqual(payload.context, publish_payload.context) def test_floatingip_update_callback(self): new_fip = {'floating_ip_address': netaddr.IPAddress('10.10.10.10'), 'router_id': '', 'floating_network_id': 'a-b-c-d-e'} old_fip = new_fip.copy() old_fip.update(router_id='old-router-id') test_context = 'test_context' get_bpg_speakers_name = '_bgp_speakers_for_gw_network_by_family' with mock.patch.object(self.plugin, get_bpg_speakers_name) as get_bgp: with mock.patch.object(self.plugin, 'stop_route_advertisements') as stop_ad: with mock.patch.object(self.plugin, '_bgp_rpc') as bgp_rpc: bgp_speaker = mock.Mock() bgp_speaker.id = '11111111-2222-3333-4444-555555555555' get_bgp.return_value = [bgp_speaker] self.plugin.floatingip_update_callback( test_context, events.AFTER_UPDATE, None, payload=events.DBEventPayload( test_context, states=(old_fip, new_fip))) get_bgp.assert_called_once_with(self.fake_admin_ctx, 'a-b-c-d-e', n_const.IP_VERSION_4) old_host_route = [{'destination': '10.10.10.10/32', 'next_hop': None}] stop_ad.assert_called_once_with(self.fake_admin_ctx, bgp_rpc, bgp_speaker.id, old_host_route) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing/version.py0000664000175000017500000000127600000000000025427 0ustar00zuulzuul00000000000000# Copyright 2011 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pbr.version version_info = pbr.version.VersionInfo('neutron_dynamic_routing') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3926604 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/0000775000175000017500000000000000000000000025054 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/PKG-INFO0000664000175000017500000000374000000000000026155 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: neutron-dynamic-routing Version: 20.0.0 Summary: Neutron Dynamic Routing Home-page: https://docs.openstack.org/neutron-dynamic-routing/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: Team and repository tags ======================== .. image:: http://governance.openstack.org/badges/neutron-dynamic-routing.svg :target: http://governance.openstack.org/reference/tags/index.html .. Change things from this point on This package contains neutron-dynamic-routing code which depends upon neutron and it's related libraries to run. Project Resources ================= The homepage for Neutron is: https://launchpad.net/neutron. Use this site for asking for help, and filing bugs. We use a single launchpad page for all Neutron projects. Code is available on opendev.org at: https://opendev.org/openstack/neutron-dynamic-routing Refer to Neutron documentation for more information: `Neutron README.rst `_ Release notes for the project can be found at: https://docs.openstack.org/releasenotes/neutron-dynamic-routing/ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=3.6 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/SOURCES.txt0000664000175000017500000001741000000000000026743 0ustar00zuulzuul00000000000000.coveragerc .mailmap .pylintrc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst TESTING.rst lower-constraints.txt requirements.txt setup.cfg setup.py test-requirements.txt tox.ini devstack/README.rst devstack/plugin.sh devstack/settings devstack/lib/dr doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/_static/.placeholder doc/source/admin/agent-scheduler.rst doc/source/admin/bgp-speaker.rst doc/source/admin/index.rst doc/source/admin/route-advertisement.rst doc/source/admin/system-design.rst doc/source/cli/bgp-peer.rst doc/source/cli/bgp-speaker.rst doc/source/cli/dynamic-routing-agent.rst doc/source/cli/index.rst doc/source/configuration/bgp_dragent.rst doc/source/configuration/index.rst doc/source/configuration/policy-sample.rst doc/source/configuration/policy.rst doc/source/configuration/samples/bgp_dragent.rst doc/source/contributor/contributing.rst doc/source/contributor/dragent-drivers.rst doc/source/contributor/index.rst doc/source/contributor/testing.rst doc/source/install/index.rst doc/source/reference/index.rst etc/README.txt etc/oslo-config-generator/bgp_dragent.ini etc/oslo-policy-generator/policy.conf neutron_dynamic_routing/__init__.py neutron_dynamic_routing/_i18n.py neutron_dynamic_routing/version.py neutron_dynamic_routing.egg-info/PKG-INFO neutron_dynamic_routing.egg-info/SOURCES.txt neutron_dynamic_routing.egg-info/dependency_links.txt neutron_dynamic_routing.egg-info/entry_points.txt neutron_dynamic_routing.egg-info/not-zip-safe neutron_dynamic_routing.egg-info/pbr.json neutron_dynamic_routing.egg-info/requires.txt neutron_dynamic_routing.egg-info/top_level.txt neutron_dynamic_routing/api/__init__.py neutron_dynamic_routing/api/rpc/__init__.py neutron_dynamic_routing/api/rpc/agentnotifiers/__init__.py neutron_dynamic_routing/api/rpc/agentnotifiers/bgp_dr_rpc_agent_api.py neutron_dynamic_routing/api/rpc/callbacks/__init__.py neutron_dynamic_routing/api/rpc/callbacks/resources.py neutron_dynamic_routing/api/rpc/handlers/__init__.py neutron_dynamic_routing/api/rpc/handlers/bgp_speaker_rpc.py neutron_dynamic_routing/cmd/__init__.py neutron_dynamic_routing/cmd/eventlet/__init__.py neutron_dynamic_routing/cmd/eventlet/agents/__init__.py neutron_dynamic_routing/cmd/eventlet/agents/bgp_dragent.py neutron_dynamic_routing/db/__init__.py neutron_dynamic_routing/db/bgp_db.py neutron_dynamic_routing/db/bgp_dragentscheduler_db.py neutron_dynamic_routing/db/migration/README neutron_dynamic_routing/db/migration/__init__.py neutron_dynamic_routing/db/migration/alembic_migrations/__init__.py neutron_dynamic_routing/db/migration/alembic_migrations/env.py neutron_dynamic_routing/db/migration/alembic_migrations/script.py.mako neutron_dynamic_routing/db/migration/alembic_migrations/versions/CONTRACT_HEAD neutron_dynamic_routing/db/migration/alembic_migrations/versions/EXPAND_HEAD neutron_dynamic_routing/db/migration/alembic_migrations/versions/start_neutron_dynamic_routing.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/4cf8bc3edb66_rename_tenant_to_project.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/contract/61cc795e43e8_initial.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/newton/expand/f399fa0f5f25_initial.py neutron_dynamic_routing/db/migration/alembic_migrations/versions/queens/contract/a589fdb5724c_change_size_of_as_number.py neutron_dynamic_routing/db/migration/models/__init__.py neutron_dynamic_routing/db/migration/models/head.py neutron_dynamic_routing/extensions/__init__.py neutron_dynamic_routing/extensions/bgp.py neutron_dynamic_routing/extensions/bgp_4byte_asn.py neutron_dynamic_routing/extensions/bgp_dragentscheduler.py neutron_dynamic_routing/policies/__init__.py neutron_dynamic_routing/policies/base.py neutron_dynamic_routing/policies/bgp_dragent.py neutron_dynamic_routing/policies/bgp_peer.py neutron_dynamic_routing/policies/bgp_speaker.py neutron_dynamic_routing/services/__init__.py neutron_dynamic_routing/services/bgp/__init__.py neutron_dynamic_routing/services/bgp/bgp_plugin.py neutron_dynamic_routing/services/bgp/agent/__init__.py neutron_dynamic_routing/services/bgp/agent/bgp_dragent.py neutron_dynamic_routing/services/bgp/agent/config.py neutron_dynamic_routing/services/bgp/agent/entry.py neutron_dynamic_routing/services/bgp/agent/driver/__init__.py neutron_dynamic_routing/services/bgp/agent/driver/base.py neutron_dynamic_routing/services/bgp/agent/driver/exceptions.py neutron_dynamic_routing/services/bgp/agent/driver/utils.py neutron_dynamic_routing/services/bgp/agent/driver/os_ken/__init__.py neutron_dynamic_routing/services/bgp/agent/driver/os_ken/driver.py neutron_dynamic_routing/services/bgp/common/__init__.py neutron_dynamic_routing/services/bgp/common/constants.py neutron_dynamic_routing/services/bgp/common/opts.py neutron_dynamic_routing/services/bgp/scheduler/__init__.py neutron_dynamic_routing/services/bgp/scheduler/bgp_dragent_scheduler.py neutron_dynamic_routing/tests/__init__.py neutron_dynamic_routing/tests/common/__init__.py neutron_dynamic_routing/tests/common/helpers.py neutron_dynamic_routing/tests/functional/__init__.py neutron_dynamic_routing/tests/functional/services/__init__.py neutron_dynamic_routing/tests/functional/services/bgp/__init__.py neutron_dynamic_routing/tests/functional/services/bgp/scheduler/__init__.py neutron_dynamic_routing/tests/functional/services/bgp/scheduler/test_bgp_dragent_scheduler.py neutron_dynamic_routing/tests/unit/__init__.py neutron_dynamic_routing/tests/unit/api/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/agentnotifiers/test_bgp_dr_rpc_agent_api.py neutron_dynamic_routing/tests/unit/api/rpc/handlers/__init__.py neutron_dynamic_routing/tests/unit/api/rpc/handlers/test_bgp_speaker_rpc.py neutron_dynamic_routing/tests/unit/db/__init__.py neutron_dynamic_routing/tests/unit/db/test_bgp_db.py neutron_dynamic_routing/tests/unit/db/test_bgp_dragentscheduler_db.py neutron_dynamic_routing/tests/unit/services/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/test_bgp_plugin.py neutron_dynamic_routing/tests/unit/services/bgp/agent/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/agent/test_bgp_dragent.py neutron_dynamic_routing/tests/unit/services/bgp/driver/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/driver/test_utils.py neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/driver/os_ken/test_driver.py neutron_dynamic_routing/tests/unit/services/bgp/scheduler/__init__.py neutron_dynamic_routing/tests/unit/services/bgp/scheduler/test_bgp_dragent_scheduler.py releasenotes/notes/.placeholder releasenotes/notes/add-static-scheduler-a3b0f54b964ae306.yaml releasenotes/notes/drop-py27-support-795303ca12cccd34.yaml releasenotes/notes/dvr-aware-announcements-24bfcb8fee87161d.yaml releasenotes/notes/mp-bgp-support-d408e8569e94d07f.yaml releasenotes/notes/rehome-dynamic-routing-apidef-d656e3273baac4e8.yaml releasenotes/notes/support-4byte-asn-d89d7100c0890ebf.yaml releasenotes/source/README.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/train.rst releasenotes/source/unreleased.rst releasenotes/source/ussuri.rst releasenotes/source/victoria.rst releasenotes/source/wallaby.rst releasenotes/source/xena.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder tools/check_unit_test_structure.sh tools/clean.sh tools/generate_config_file_samples.sh././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/dependency_links.txt0000664000175000017500000000000100000000000031122 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/entry_points.txt0000664000175000017500000000112100000000000030345 0ustar00zuulzuul00000000000000[console_scripts] neutron-bgp-dragent = neutron_dynamic_routing.cmd.eventlet.agents.bgp_dragent:main [neutron.db.alembic_migrations] neutron-dynamic-routing = neutron_dynamic_routing.db.migration:alembic_migrations [neutron.policies] neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules [neutron.service_plugins] bgp = neutron_dynamic_routing.services.bgp.bgp_plugin:BgpPlugin [oslo.config.opts] bgp.agent = neutron_dynamic_routing.services.bgp.common.opts:list_bgp_agent_opts [oslo.policy.policies] neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/not-zip-safe0000664000175000017500000000000100000000000027302 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/pbr.json0000664000175000017500000000005600000000000026533 0ustar00zuulzuul00000000000000{"git_version": "3d5caba", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/requires.txt0000664000175000017500000000051400000000000027454 0ustar00zuulzuul00000000000000SQLAlchemy>=1.3.3 alembic>=0.9.6 eventlet!=0.18.3,!=0.20.1,>=0.18.2 httplib2>=0.9.1 netaddr>=0.7.18 neutron-lib>=1.26.0 neutron>=14.0.0.0b1 os-ken>=0.3.0 oslo.config>=5.2.0 oslo.db>=4.44.0 oslo.log>=3.36.0 oslo.messaging>=5.29.0 oslo.serialization!=2.19.1,>=2.18.0 oslo.service!=1.28.1,>=1.24.0 oslo.utils>=4.5.0 pbr!=2.1.0,>=2.0.0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641159.0 neutron-dynamic-routing-20.0.0/neutron_dynamic_routing.egg-info/top_level.txt0000664000175000017500000000003000000000000027577 0ustar00zuulzuul00000000000000neutron_dynamic_routing ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.3846602 neutron-dynamic-routing-20.0.0/releasenotes/0000775000175000017500000000000000000000000021106 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/releasenotes/notes/0000775000175000017500000000000000000000000022236 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000000000000000024507 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/add-static-scheduler-a3b0f54b964ae306.yaml0000664000175000017500000000077500000000000031332 0ustar00zuulzuul00000000000000--- features: - | A new BGP scheduler has been added, called ``StaticScheduler``. It will not perform any automatic scheduling of speakers to agents, instead relying on API calls to perform explicit scheduling, fulfilling the needs of larger deployments. See also bug `1920065 `_. The plan is to make the ``StaticScheduler`` the default option for the next release and possibly deprecate the current default, ``ChanceScheduler``. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/drop-py27-support-795303ca12cccd34.yaml0000664000175000017500000000036000000000000030601 0ustar00zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. Last release of neutron-dynamic-routing to support python 2.7 is OpenStack Train. The minimum version of Python now supported by neutron-dynamic-routing is Python 3.6. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/dvr-aware-announcements-24bfcb8fee87161d.yaml0000664000175000017500000000055200000000000032252 0ustar00zuulzuul00000000000000--- features: - | DVR-aware BGP announcements are now supported for IPv4. Host routes for instances are announced as /32 host routes, using the appropriate floating IP gateway port on the host as the next-hop. This allows network traffic to bypass the centralized router on the network node and ingress/egress directly on the compute node. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/mp-bgp-support-d408e8569e94d07f.yaml0000664000175000017500000000040100000000000030172 0ustar00zuulzuul00000000000000--- features: - | The os-ken driver now supports announcement of IPv6 prefixes over IPv4 sessions and vice-versa. Peers can opt in/out with MP-BGP capabilities configured on the peer end of BGP sessions established with the os-ken driver. ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-dynamic-routing-20.0.0/releasenotes/notes/rehome-dynamic-routing-apidef-d656e3273baac4e8.yaml 22 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/rehome-dynamic-routing-apidef-d656e3273baac4e8.yam0000664000175000017500000000027600000000000033065 0ustar00zuulzuul00000000000000--- features: - The API definitions of ``neutron-dynamic-routing``, ``bgp``, ``bgp_4byte_asn`` and ``bgp_dragent_scheduler``, are now available in ``neutron_lib.api.definitions``. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/notes/support-4byte-asn-d89d7100c0890ebf.yaml0000664000175000017500000000011500000000000030656 0ustar00zuulzuul00000000000000--- features: - The neutron-dynamic-routing supports 4-byte AS Numbers now.././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/releasenotes/source/0000775000175000017500000000000000000000000022406 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/README.rst0000664000175000017500000000104300000000000024073 0ustar00zuulzuul00000000000000=========================================== Neutron Dynamic Routing Release Notes Howto =========================================== Release notes are a new feature for documenting new features in OpenStack projects. Background on the process, tooling, and methodology is documented in a `mailing list post by Doug Hellman `_. For information on how to create release notes, please consult the `Release Notes documentation `_. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/releasenotes/source/_static/0000775000175000017500000000000000000000000024034 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000000000000000026305 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/releasenotes/source/_templates/0000775000175000017500000000000000000000000024543 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000000000000000027014 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/conf.py0000664000175000017500000002166400000000000023716 0ustar00zuulzuul00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # Neutron Dynamic Routing Release Notes documentation build configuration file, # created by # sphinx-quickstart on Tue Nov 3 17:40:50 2015. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'openstackdocstheme', 'reno.sphinxext', ] # openstackdocstheme options openstackdocs_repo_name = 'neutron-dynamic-routing' openstackdocs_auto_name = False openstackdocs_bug_project = 'neutron' openstackdocs_bug_tag = 'doc' # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Neutron Dynamic Routing Release Notes' copyright = '2015, Neutron Dynamic Routing Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'native' # 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 true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronDynamicRoutingReleaseNotesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ ('index', 'NeutronDynamicRoutingReleaseNotes.tex', 'Neutron Dynamic Routing Release Notes Documentation', 'Neutron Dynamic Routing Developers', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # If true, show page references after internal links. # latex_show_pagerefs = False # If true, show URL addresses after external links. # latex_show_urls = False # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'neutrondynamicroutingreleasenotes', 'Neutron Dynamic Routing', ' Release Notes Documentation', ['Neutron Dynamic Routing Developers'], 1) ] # If true, show URL addresses after external links. # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'NeutronDynamicRoutingReleaseNotes', 'Neutron Dynamic Routing', ' Release Notes Documentation', 'Neutron Dynamic Routing Developers', 'NeutronDynamicRoutingReleaseNotes', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # texinfo_appendices = [] # If false, no module index is generated. # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # texinfo_no_detailmenu = False # -- Options for Internationalization output ------------------------------ locale_dirs = ['locale/'] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/index.rst0000664000175000017500000000043100000000000024245 0ustar00zuulzuul00000000000000====================================== Neutron Dynamic Routing Release Notes ====================================== .. toctree:: :maxdepth: 1 README.rst unreleased xena wallaby victoria ussuri train stein rocky queens pike ocata newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/newton.rst0000664000175000017500000000023200000000000024447 0ustar00zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/ocata.rst0000664000175000017500000000023000000000000024222 0ustar00zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/pike.rst0000664000175000017500000000021700000000000024070 0ustar00zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/queens.rst0000664000175000017500000000022300000000000024435 0ustar00zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022100000000000024262 0ustar00zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/stein.rst0000664000175000017500000000022100000000000024255 0ustar00zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/train.rst0000664000175000017500000000017600000000000024261 0ustar00zuulzuul00000000000000========================== Train Series Release Notes ========================== .. release-notes:: :branch: stable/train ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000015600000000000025271 0ustar00zuulzuul00000000000000============================= Current Series Release Notes ============================= .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/ussuri.rst0000664000175000017500000000020200000000000024464 0ustar00zuulzuul00000000000000=========================== Ussuri Series Release Notes =========================== .. release-notes:: :branch: stable/ussuri ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/victoria.rst0000664000175000017500000000021200000000000024753 0ustar00zuulzuul00000000000000============================= Victoria Series Release Notes ============================= .. release-notes:: :branch: stable/victoria ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/wallaby.rst0000664000175000017500000000020600000000000024571 0ustar00zuulzuul00000000000000============================ Wallaby Series Release Notes ============================ .. release-notes:: :branch: stable/wallaby ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/releasenotes/source/xena.rst0000664000175000017500000000017200000000000024073 0ustar00zuulzuul00000000000000========================= Xena Series Release Notes ========================= .. release-notes:: :branch: stable/xena ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/requirements.txt0000664000175000017500000000200000000000000021671 0ustar00zuulzuul00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr!=2.1.0,>=2.0.0 # Apache-2.0 eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT httplib2>=0.9.1 # MIT netaddr>=0.7.18 # BSD SQLAlchemy>=1.3.3 # MIT alembic>=0.9.6 # MIT neutron-lib>=1.26.0 # Apache-2.0 os-ken>=0.3.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.db>=4.44.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 oslo.utils>=4.5.0 # Apache-2.0 neutron>=14.0.0.0b1 # Apache-2.0 # The comment below indicates this project repo is current with neutron-lib # and should receive neutron-lib consumption patches as they are released # in neutron-lib. It also implies the project will stay current with TC # and infra initiatives ensuring consumption patches can land. # neutron-lib-current ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/setup.cfg0000664000175000017500000000264500000000000020245 0ustar00zuulzuul00000000000000[metadata] name = neutron-dynamic-routing summary = Neutron Dynamic Routing description_file = README.rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/neutron-dynamic-routing/latest/ python_requires = >=3.6 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: Implementation :: CPython Programming Language :: Python :: 3 :: Only Programming Language :: Python :: 3 Programming Language :: Python :: 3.6 Programming Language :: Python :: 3.7 [files] packages = neutron_dynamic_routing [entry_points] console_scripts = neutron-bgp-dragent = neutron_dynamic_routing.cmd.eventlet.agents.bgp_dragent:main neutron.db.alembic_migrations = neutron-dynamic-routing = neutron_dynamic_routing.db.migration:alembic_migrations oslo.config.opts = bgp.agent = neutron_dynamic_routing.services.bgp.common.opts:list_bgp_agent_opts oslo.policy.policies = neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules neutron.policies = neutron-dynamic-routing = neutron_dynamic_routing.policies:list_rules neutron.service_plugins = bgp = neutron_dynamic_routing.services.bgp.bgp_plugin:BgpPlugin [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/setup.py0000664000175000017500000000127100000000000020130 0ustar00zuulzuul00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. import setuptools setuptools.setup( setup_requires=['pbr>=2.0.0'], pbr=True) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/test-requirements.txt0000664000175000017500000000120700000000000022656 0ustar00zuulzuul00000000000000# 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>=3.2.0,<3.3.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 fixtures>=3.0.0 # Apache-2.0/BSD flake8-import-order==0.12 # LGPLv3 python-subunit>=1.0.0 # Apache-2.0/BSD oslo.concurrency>=3.26.0 # Apache-2.0 stestr>=1.0.0 # Apache-2.0 testresources>=2.0.0 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD WebOb>=1.8.2 # MIT WebTest>=2.0.27 # MIT oslotest>=3.2.0 # Apache-2.0 tempest>=17.1.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1648641159.4046607 neutron-dynamic-routing-20.0.0/tools/0000775000175000017500000000000000000000000017555 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/tools/check_unit_test_structure.sh0000775000175000017500000000312000000000000025403 0ustar00zuulzuul00000000000000#!/usr/bin/env bash # This script identifies the unit test modules that do not correspond # directly with a module in the code tree. See TESTING.rst for the # intended structure. neutron_path=$(cd "$(dirname "$0")/.." && pwd) base_test_path=neutron_dynamic_routing/tests/unit test_path=$neutron_path/$base_test_path test_files=$(find ${test_path} -iname 'test_*.py') ignore_regexes=( "^plugins.*$" ) error_count=0 ignore_count=0 total_count=0 for test_file in ${test_files[@]}; do relative_path=${test_file#$test_path/} expected_path=$(dirname $neutron_path/neutron_dynamic_routing/$relative_path) test_filename=$(basename "$test_file") expected_filename=${test_filename#test_} # Module filename (e.g. foo/bar.py -> foo/test_bar.py) filename=$expected_path/$expected_filename # Package dir (e.g. foo/ -> test_foo.py) package_dir=${filename%.py} if [ ! -f "$filename" ] && [ ! -d "$package_dir" ]; then for ignore_regex in ${ignore_regexes[@]}; do if [[ "$relative_path" =~ $ignore_regex ]]; then ((ignore_count++)) continue 2 fi done echo "Unexpected test file: $base_test_path/$relative_path" ((error_count++)) fi ((total_count++)) done if [ "$ignore_count" -ne 0 ]; then echo "$ignore_count unmatched test modules were ignored" fi if [ "$error_count" -eq 0 ]; then echo 'Success! All test modules match targets in the code tree.' exit 0 else echo "Failure! $error_count of $total_count test modules do not match targets in the code tree." exit 1 fi ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/tools/clean.sh0000775000175000017500000000030400000000000021173 0ustar00zuulzuul00000000000000#!/usr/bin/env bash rm -rf ./*.deb ./*.tar.gz ./*.dsc ./*.changes rm -rf */*.deb rm -rf ./plugins/**/build/ ./plugins/**/dist rm -rf ./plugins/**/lib/neutron_*_plugin.egg-info ./plugins/neutron-* ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/tools/generate_config_file_samples.sh0000775000175000017500000000144000000000000025755 0ustar00zuulzuul00000000000000#!/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 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1648641129.0 neutron-dynamic-routing-20.0.0/tox.ini0000664000175000017500000001023200000000000017726 0ustar00zuulzuul00000000000000[tox] envlist = py3,pep8,pylint minversion = 3.1.1 skipsdist = True [testenv] setenv = VIRTUAL_ENV={envdir} OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true} OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true} OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true} PYTHONWARNINGS=default::DeprecationWarning usedevelop = True install_command =pip install {opts} {packages} deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt whitelist_externals = find sh commands = find . -type f -name "*.py[c|o]" -delete find . -type l -name "*.py[c|o]" -delete find . -depth -path "*/__pycache__*" -delete stestr run {posargs} # there is also secret magic in stestr which lets you run in a fail only # mode. To do this define the TRACE_FAILONLY environmental variable. [testenv:functional] setenv = OS_TEST_PATH=./neutron_dynamic_routing/tests/functional commands = stestr run {posargs} stestr slowest [testenv:releasenotes] deps = {[testenv:docs]deps} commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pep8] commands = flake8 neutron-db-manage --subproject neutron-dynamic-routing --database-connection sqlite:// check_migration {[testenv:genconfig]commands} {[testenv:genpolicy]commands} [testenv:cover] setenv = {[testenv]setenv} PYTHON=coverage run --source neutron_dynamic_routing --parallel-mode commands = stestr run --no-subunit-trace {posargs} coverage combine coverage html -d cover coverage xml -o cover/coverage.xml [testenv:venv] commands = {posargs} [testenv:docs] deps = -c{env:TOX_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 [flake8] # E125 continuation line does not distinguish itself from next logical line # E126 continuation line over-indented for hanging indent # E128 continuation line under-indented for visual indent # E129 visually indented line with same indent as next logical line # E265 block comment should start with ‘# ‘ # H405 multi line docstring summary not separated with an empty line # TODO(marun) H404 multi line docstring should start with a summary # N530 direct neutron imports not allowed # TODO(ihrachys) -- reenable N537 when new neutron-lib release is available # N537 Log messages should not be translated # H106 Don’t put vim configuration in source files # H203 Use assertIs(Not)None to check for None # W504 line break after binary operator # (W503 and W504 are incompatible and we need to choose one of them. # Existing codes follows W503, so we disable W504.) ignore = E125,E126,E128,E129,E265,H404,H405,N530,N537,W504 enable-extensions=H106,H203 show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,tools,.ropeproject,rally-scenarios import-order-style = pep8 [testenv:pylint] deps = {[testenv]deps} pylint commands = pylint --rcfile=.pylintrc --output-format=colorized {posargs:neutron_dynamic_routing} [hacking] import_exceptions = neutron_dynamic_routing._i18n local-check-factory = neutron_lib.hacking.checks.factory [testenv:genconfig] commands = {toxinidir}/tools/generate_config_file_samples.sh [testenv:genpolicy] commands = oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf [testenv:lower-constraints] deps = -c{toxinidir}/lower-constraints.txt -r{toxinidir}/test-requirements.txt -r{toxinidir}/requirements.txt [testenv:dev] # run locally (not in the gate) using editable mode # https://pip.pypa.io/en/stable/reference/pip_install/#editable-installs commands = pip install -q -e "git+https://opendev.org/openstack/neutron#egg=neutron" [testenv:py-dev] commands = {[testenv:dev]commands} {[testenv]commands} [testenv:pep8-dev] deps = {[testenv]deps} commands = {[testenv:dev]commands} {[testenv:pep8]commands}