././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.341352 neutron-fwaas-20.0.0/0000775000175000017500000000000000000000000014405 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/.coveragerc0000664000175000017500000000014700000000000016530 0ustar00zuulzuul00000000000000[run] branch = True source = neutron_fwaas omit = neutron_fwaas/tests/* [report] ignore_errors = True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/.mailmap0000664000175000017500000000111600000000000016025 0ustar00zuulzuul00000000000000# Format is: # # lawrancejing Jiajun Liu Zhongyue Luo Kun Huang Zhenguo Niu Isaku Yamahata Isaku Yamahata Morgan Fainberg ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/.pylintrc0000664000175000017500000000664300000000000016263 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 lowercased with underscores method-rgx=([a-z_][a-z0-9_]{2,}|setUp|tearDown)$ # Module names matching neutron-* are ok (files in bin/) module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+)|(neutron-[a-z0-9_-]+))$ # Don't require docstrings on tests. no-docstring-rgx=((__.*__)|([tT]est.*)|setUp|tearDown)$ [FORMAT] # Maximum number of characters on a single line. max-line-length=79 [VARIABLES] # List of additional names supposed to be defined in builtins. Remember that # you should avoid to define new builtins when possible. # _ is used by our localization additional-builtins=_ [CLASSES] # List of interface methods to ignore, separated by a comma. ignore-iface-methods= [IMPORTS] # Deprecated modules which should not be used, separated by a comma deprecated-modules= # should use oslo_serialization.jsonutils json [TYPECHECK] # List of module names for which member attributes should not be checked ignored-modules=six.moves,_MovedItems [REPORTS] # Tells whether to display a full report or only the messages reports=no ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/.stestr.conf0000664000175000017500000000011300000000000016651 0ustar00zuulzuul00000000000000[DEFAULT] test_path=${OS_TEST_PATH:-./neutron_fwaas/tests/unit} top_dir=./ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/.zuul.yaml0000664000175000017500000000712400000000000016352 0ustar00zuulzuul00000000000000- job: name: neutron-fwaas-openstack-tox-py310-with-sqlalchemy-main required-projects: - name: github.com/sqlalchemy/sqlalchemy override-checkout: main - name: github.com/sqlalchemy/alembic override-checkout: main - openstack/oslo.db - openstack/neutron - openstack/neutron-lib parent: openstack-tox-py310-with-oslo-master - project: templates: - check-requirements - openstack-cover-jobs-neutron - openstack-python3-jobs-neutron - periodic-stable-jobs-neutron - publish-openstack-docs-pti - release-notes-jobs-python3 check: jobs: - openstack-tox-docs: required-projects: - openstack/neutron-lib - openstack/neutron - neutron-fwaas-functional - neutron-tempest-plugin-fwaas - neutron-fwaas-v2-dsvm-tempest-multinode gate: jobs: - openstack-tox-docs: required-projects: - openstack/neutron-lib - openstack/neutron - neutron-fwaas-functional - neutron-tempest-plugin-fwaas experimental: jobs: - neutron-fwaas-fullstack - openstack-tox-py310-with-oslo-master - neutron-fwaas-openstack-tox-py310-with-sqlalchemy-main periodic-weekly: jobs: - openstack-tox-py311 - openstack-tox-py310-with-oslo-master - neutron-fwaas-openstack-tox-py310-with-sqlalchemy-main - neutron-fwaas-functional - neutron-tempest-plugin-fwaas - job: name: neutron-fwaas-functional parent: neutron-functional timeout: 2400 pre-run: playbooks/configure_functional_job.yaml vars: project_name: neutron-fwaas devstack_services: INSTALL_OVN: false - job: name: neutron-fwaas-fullstack parent: neutron-fullstack vars: project_name: neutron-fwaas - job: name: neutron-fwaas-v2-dsvm-tempest-multinode parent: neutron-ovs-tempest-multinode-full roles: - zuul: openstack/devstack required-projects: - openstack/devstack-gate - openstack/neutron - openstack/neutron-fwaas - openstack/neutron-tempest-plugin - openstack/tempest vars: tox_envlist: all tempest_test_regex: '^(neutron_tempest_plugin.fwaas)' devstack_plugins: neutron: https://opendev.org/openstack/neutron.git neutron-fwaas: https://opendev.org/openstack/neutron-fwaas.git neutron-tempest-plugin: https://opendev.org/openstack/neutron-tempest-plugin.git tempest_plugins: - neutron-tempest-plugin devstack_services: q-fwaas-v2: true devstack_localrc: NETWORK_API_EXTENSIONS: "agent,binding,dhcp_agent_scheduler,external-net,ext-gw-mode,extra_dhcp_opts,quotas,router,security-group,subnet_allocation,network-ip-availability,auto-allocated-topology,timestamp_core,tag,service-type,rbac-policies,standard-attr-description,pagination,sorting,project-id,fwaas_v2" Q_AGENT: openvswitch Q_ML2_TENANT_NETWORK_TYPE: vxlan Q_ML2_PLUGIN_MECHANISM_DRIVERS: openvswitch group-vars: subnode: devstack_services: q-agt: true devstack_localrc: USE_PYTHON3: true devstack_local_conf: post-config: # NOTE(slaweq): We can get rid of this hardcoded absolute path when # devstack-tempest job will be switched to use lib/neutron instead of # lib/neutron-legacy "/$NEUTRON_CORE_PLUGIN_CONF": ovs: tunnel_bridge: br-tun bridge_mappings: public:br-ex ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/AUTHORS0000664000175000017500000003042400000000000015460 0ustar00zuulzuul0000000000000025643 Aaron Rosen Abhishek Raut Adam Harwell Adit Sarfaty Akash Gangil Akihiro MOTOKI Akihiro Motoki Akihiro Motoki Aleks Chirko Alessandro Pilotti Alessio Ababilov Alessio Ababilov Alfredo Moralejo Amir Sadoughi Andre Pech Andrea Frittoli Andreas Jaeger Andreas Jaeger Angus Lees Ann Kamyshnikova Anusha Ramineni Aradhana Singh Armando Migliaccio Artur Korzeniewski Arvind Somy Arvind Somya Assaf Muller Benedikt Trefzer Bertrand Lallau Bertrand Lallau Bhuvan Arumugam Bob Kukura Bob Melander Boden R Brad Hall Brandon Logan Brandon Logan Brant Knudson Brian Haley Brian Waldon Cao Xuan Hoang Carl Baldwin Cedric Brandily Chandan Dutta Chowdhury Chang Bo Guo ChangBo Guo(gcb) Christian Berendt Chuck Short Clark Boylan Clint Byrum Corey Bryant Cuong Nguyen Cyril Roelandt Dan Prince Dan Wendlandt Dao Cong Tien Davanum Srinivas Davanum Srinivas Dave Lapsley David Homolka Deepak N Dirk Mueller Dongcan Ye Doug Hellmann Doug Hellmann Doug Wiegley Duan Jiong Edgar Magana Elena Ezhova Emilien Macchi Eugene Nikanorov Flavio Percoco Gary Kotton Gary Kotton German Eichberger German Eichberger Ghanshyam Mann Gordon Chung Guilherme Salgado Ha Van Tu Hareesh Puthalath Harkirat Singh He Jie Xu Hemanth Ravi Henry Gessau Henry Gessau Henry Gessau HenryVIII Hirofumi Ichihara Hiroyuki Ito Hongbin Lu Hunt Xu Ian Wienand Ignacio Scopetta Ihar Hrachyshka Inessa Vasilevskaya Ionuț Arțăriși Irena Berezovsky Isaku Yamahata Isaku Yamahata Itsuro Oda JJ Asghar Jacek Swiderski Jakub Libosvar James Arendt James E. Blair James E. Blair James Page Jason Kölker Jay Pipes Jeremy Stanley Jiajun Liu Joe Gordon Joe Heck John Dunning Jordan Tardif Juliano Martinez Julien Danjou Justin Lund KIYOHIRO ADACHI Ken'ichi Ohmichi Keshava Bharadwaj Kevin Benton Kevin L. Mitchell Kim Bao Long Koteswara Rao Kelam Koteswara Rao Kelam Kris Lindgren Kun Huang Kyle Mestery Kyle Mestery Li Ma LiuNanke Lucian Petrut Luke Gorrie Lukáš Piwowarski Luong Anh Tuan Major Hayden Margaret Frances Mark McClain Mark McClain Mark McLoughlin Martin Hickey Maru Newby Maru Newby Mate Lakat Mathieu Rohon Matt Riedemann Matthew Treinish Miguel Angel Ajo Mohammad Banikazemi Monty Taylor Morgan Fainberg Motohiro OTSUKA Nachi Ueno Nachi Ueno Nader Lahouti Nam Nguyen Hoai Nate Johnston Nate Johnston Nguyen Hai Nguyen Hai Truong Nguyen Hung Phuong Nguyen Phuong An Nguyen Thanh Cong Nguyen Van Trung Nick Numan Siddique Nurmatov Mamatisa Oleg Bondarev Ondřej Nový OpenStack Release Bot Paddu Krishnan Paul Michali Paul Michali Praneet Bachheti Preeti Mirji Qian Min Chen Rajaram Mallya Rajesh Mohan Rajesh Mohan Ralf Haferkamp Reedip Reedip Reedip Reedip Banerjee Rich Curran Rocky Rodolfo Alonso Hernandez Roman Podoliaka Romil Gupta Rui Zang Russell Bryant Ryota MIBU Salvatore Orlando Salvatore Orlando Samer Deeb Santhosh Santhosh Kumar Sarath Chandra Mekala Sascha Peilicke Sascha Peilicke Sascha Peilicke Sean Dague Sean Dague Sean M. Collins Sean M. Collins Sean McGinnis Sergey Lukjanov Sergey Skripnick Shiv Haris Shivakumar M Simon Murray Sirushti Murugesan Slawek Kaplonski Somik Behera Somik Behera SongmingYan Sridar Kandaswamy Sukhdev Sumit Naiksatam Sumit Naiksatam Sushil Kumar Sylvain Afchain Szymon Datko Sławek Kapłoński Takashi Kajinami Terry Wilson Thierry Carrez Thomas Bechtold Thomas Morin Tim Miller Tim Swanson Tony Breeds Tony Xu Toshiaki Higuchi Trinath Somanchi TrinathSomanchi Tyler Smith Van Hung Pham Vikash082 Vishwanath Jayaraman Vu Cong Tuan Weidong Shao Wu Wenxiang Xing Zhang Xuhan Peng YAMAMOTO Takashi YAMAMOTO Takashi Yaguang Tang Yalei Wang Yang Youseok Yann Morice Yanping Qu Yi Zhao Ying Liu Yong Sheng Gong Yong Sheng Gong Yoshihiro Kaneko Yushiro FURUKAWA Zang MingJie ZhaoBo Zhenguo Niu Zhenmei ZhiQiang Fan ZhiQiang Fan Zhongyue Luo ZhouHeng alexpilotti armando-migliaccio armando-migliaccio badveli_vishnuus berlin caoyuan da52700 drzix elajkat fujioka yuuichi fumihiko kakuma garyduan ghanshyam gongysh gongysh goocher gordon chung huanghuijin inspurericzhang justin Lund lawrancejing likui liu-sheng liuqing liushy llg8212 loooosy madhusudhan-kandadai malei maliangyi mark mcclain mathieu-rohon qinhaizhong01 quyue reedip ricolin ritesh.arya rohitagarwalla ronak root rossella shanyunfan33 shihanzhang snaiksat sridhargaddam sukhdev sunxifa takanorimiyagishi trinaths vinkesh banka wanghui wujun yatinkarel zhangdebo1987 zhangguoqing zhanghao zhanghao2 zhangtongjian <125163227@qq.com> zhangyanxian zhhuabj zhouhenglc zhufl zhulingjie zoukeke Édouard Thuleau ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/CONTRIBUTING.rst0000664000175000017500000000027500000000000017052 0ustar00zuulzuul00000000000000Please see the Neutron CONTRIBUTING.rst file for how to contribute to neutron-fwaas: `Neutron CONTRIBUTING.rst `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145113.0 neutron-fwaas-20.0.0/ChangeLog0000664000175000017500000017163000000000000016167 0ustar00zuulzuul00000000000000CHANGES ======= 20.0.0 ------ * Bump hacking * tox: Drop envdir * Update python classifier with py3.10 & py3.11 in setup.cfg * Remove usage of LBaaS constants * py311: Add py311 job and sqlalchemy-main job to weekly * [sqlalchemy-20] Replace the context writer/reader * Remove the "sitepackages" in the extra arguments * Update master for stable/2023.2 19.0.0 ------ * Use unittest.mock instead of mock * [alembic] Alembic operations require keywords only arguments * notify the plugin only on status updated * Firewall group associated with ports is not allowed to be deleted * Remove Exception Definition in fwaas\_v2 Extension * Support standard\_attrs for fwaas resources * Fix issues due to recent RBAC changes * Use neutron-lib policy rules * Update master for stable/2023.1 18.0.0 ------ * [sqlalchemy-20] The Connection.connect() method is considered legacy * Use upper-constraints in docs job * Add CONTEXT\_WRITER to \_get\_default\_fwg\_id * Make tempest-multinode job voting * Add get\_firewall\_groups\_count method * CI: add neutron and neutron-lib to docs required-projects * tox4: Add ignore\_basepython\_conflict = True to tox.ini * Fix tox.ini with tox4 * Remove note about migration from lib/neutron-legacy to lib/neutron * CI: Add openstack-tox-py39-with-oslo-master to periodic weekly queue * Remove reference to 'all-plugin' tox environment * Switch to 2023.1 Python3 unit tests and generic template name * Update master for stable/zed 17.0.0 ------ * Adopt to latest VlanManager and oslo.db changes * Use ovs constants from neutron-lib * Bump pyroute2 version to 0.7.2 * Add weekly jobs * Add libczmq4 to bindep for testing on jammy * Cleanup py27 support * Remove usage of six * setup.cfg: Replace dashes with underscores * Remove the convert\_to functionality * Remove usage of six * [doc]remove warning for "deprecate this project" * Remove "distutils" library * Add the corresponding DB context to all SQL transactions * Changed minversion in tox to 3.18.0 * Update python testing as per zed cycle teting runtime * Fix error when apply rule with dst port large than src port * Add Python3 zed unit tests * remove unicode from code * Drop lower-constraints.txt and its testing * Replace tearDown with addCleanup * Readd neutron-tempest-plugin-fwaas job * revive neutron-fwaas project * Moving IRC network reference to OFTC * Retire neutron-fwaas project * Imported Translations from Zanata 16.0.0 ------ * Deprecate neutron-fwaas as stadium project 16.0.0.0b1 ---------- * Drop Python 2 Support * setup.cfg: Cleanup * Remove os-testr leftovers * Remove fwaas\_v1 extensions * Switch to official Ussuri jobs * Remove unused import statement * PDF documentation build * Add 'none' to L3AgentExtensionAPI due to API changes 15.0.0 ------ * Default firewall group rules from configuration file * Complete move of neutron-fwaas tempest tests to tempest plugin * Ignore fwg which has no ports when restarting l3-agent * Fix bug when updating policy in firewall group * Add format string configuration for FWaaS v2 logging * Switch tempest multinode job to zuulv3 and python3 * Fix bug when removing a port from the firewall group * Switch functional tests to be run using python3 * Fix list\_entries for netlink\_lib when running on py3 * Migrate functional tests job to zuul v3 * Add Python 3 Train unit tests * Switch neutron-fwaas-v2-dsvm-tempest job to python 3 * Fix sorting of filter rules in legacy\_conntrack module * Convert legacy-neutron-fwaas-v2-dsvm-tempest job to zuulv3 * Add bindep.txt file to neutron-fwaas repo * Fix removing non-last port from the group * netfilter\_log: Correctly decode binary type prefix * Update hacking version * Switch neutron-fwaas-fullstack job to zuulv3 syntax * use logapi constants from neutron-lib * Add L3 agent extension operation tests * Dropping the py35 testing * Replace git.openstack.org URLs with opendev.org URLs * use test tools from neutron-lib * fix gate * OpenDev Migration Patch * devstack: Enable q-fwaas-v2 by default * stop using common db mixin methods * use common constants from neutron-lib * Replace openstack.org git:// URLs with https:// * Update master for stable/stein * Fix the verification method before creating and updating the firewall rule 14.0.0 ------ * Add new upgrade check * Update lower-constraints to be same as in Neutron * Migrate to stestr as unit tests runner * Change openstack-dev to openstack-discuss 14.0.0.0b2 ---------- * Fix firewall rule 'shared' check in updating fwp 14.0.0.0b1 ---------- * fix tox python3 overrides * add python 3.7 unit test job * Add service checks before trying to configure it * Remove midonet job * Fix gate issues on master * FWaaS v1->v2 DB migration * Removing FWaaS v1 source code * Change netns tests with oslo.privsep to check netns links * Define types for C calls in netlink\_lib * doc: Add policy reference * Convert policy.json into policy-in-code * Replace ryu with os\_ken * Move port validation support into the driver * ICMPv6 is not an available protocol when creating firewall rule * Switch to tempest.common.utils.requires\_ext * use neutron-lib for \_model\_query * Correct package name libnetfilter-log on CentOS * Add python 3.6 unit test job * [Trivial Fix] modify spelling error of "resource" * Update tox minversion to 2.0 * Increment versioning with pbr instruction * Adding UTs for firewall driver\_api * Subscribe Neutron port update events for FWG Logging handling * Fix associating firewall group with DVR/L3HA port * opt in for neutron-lib consumption patches * Add functional test for FWaaS L3 logging driver * use common rpc and exceptions from neutron-lib * add local tox targets for pep8 and py3 * remove external\_network\_bridge option * Use templates for cover and lower-constraints * add python 3.6 unit test job * switch documentation job to new PTI * import zuul job settings from project-config * Switch to neutron-lib version of fake\_notifier * Remove remaining NFLOG rules on deleting log resource * Should forward only first accepted packet to table 91 and 92 * Update neutron import * Add dependency for generating reno * Load neutron\_fwaas.conf with '--config-file' for neutron-server * Update reno for stable/rocky 13.0.0.0rc1 ----------- * Add releasenote for FWaaS v2 logging * [log] FWaaS L3 Logging driver based iptables * Add python binding for libnetfilter\_log * Adding resources callback handler for FWaaS v2 logging * [log]: Add rpc stuff for logging * Add log validator for FWaaS v2 logging * Adding 'logapi' in apidoc\_excluded\_paths for sphinx-docs job * Add neutron as required-projects * Add notification callback events for FWaaS v2 * Introduce accepted/dropped/rejected chains for future processing * FWaaS v2: L3 logging agent extension * Follow-up to fix some namespace tests * [FWaaS\_v2]: RPC listener should be served by rpc worker 13.0.0.0b3 ---------- * update requirements for neutron-lib 1.18.0 * use autonested\_transaction from neutron-lib * Add release note in README * Remove setting of DEVSTACK\_GATE\_EXERCISES * python3: fix netlink\_lib delete\_entries * Exclude requirements from doc8 * [FWaaS v1] RPC listener should be served by rpc worker * Imported Translations from Zanata * [Gate] Avoid sphinx\_build error and fix functional test * Check pep8 without ignoring D000 13.0.0.0b2 ---------- * Add fullstack testing for neutron-fwaas * add missing \_\_init\_\_.py file for alembic * Imported Translations from Zanata * Implements a plugable backend driver * Fix python3 dict values breakage 13.0.0.0b1 ---------- * Remove firewall (v1) shared attribute from tests * uncap eventlet * use rpc Connection rather than create\_connection * Fix pep8 new warnings * Follow the new PTI for document build * Fix incompatible requirement in lower-constraints * Remove unused plugin.get\_plugin\_name() * Fix sanity check for default firewall group name * Updated from global requirements * tests: don't rely on configuration files outside tests directory * add lower-constraints job * Updated from global requirements * Avoid tox-install.sh * DVR-FWaaS: Fix DVR FWaaS rules for fipnamespace * Updated from global requirements * Filter out conntrack entries with unknown protocol * Revert "Skip unknown protocols while deleting conntrack" * Skip unknown protocols while deleting conntrack * Imported Translations from Zanata * Make firewall l2 agent extension more generic * use is\_extension\_supported from neutron-lib * Update reno for stable/queens * Enable hacking-extensions H204, H205 12.0.0 ------ * Validating if a port is supported by FWaaS L2 driver * Remove disable option for default FWG and allow only on VM ports * Fix devstack configuration for fwaas v2 * Updated from global requirements 12.0.0.0b3 ---------- * Fixing OVS driver coexistence check * Enable to associate ports with default fwg for non-admin users * Support IPv6 for netlink\_conntrack * Updated from global requirements * Shrink Tempest scenario manager copy * Add Zuul v3 native midonet cross test * Adding new tables for future consumption * Updated from global requirements * Apply default firewall group for new VM ports * Updated from global requirements * Fix V2 update\_firewall\_group logging * use EGRESS\_DIRECTION and INGRESS\_DIRECTION from neutron-lib * Co-existing between fwg and sg * Enable to use conntrack driver in fwaas\_v2 * Add doc8 to pep8 check for neutron-fwaas project * Replace jsonutils by common json package * Imported Translations from Zanata 12.0.0.0b2 ---------- * Trivial-fix: Remove unused method * Add reno for "OVS based l2 Firewall driver for FWaaS v2" * Adding unique constraint for port\_id * Updated from global requirements * OVS based l2 Firewall driver for FWaaS v2 * FWaaS v2 extension for L2 agent * Imported Translations from Zanata * Remove setting of version/release from releasenotes * Updated from global requirements * Updated from global requirements * Updated from global requirements * Refactor \_get\_routers\_in\_project method 12.0.0.0b1 ---------- * Adopt a new abstract method ha\_state\_change * Fix UTs gate failed * Introduce default firewall groups * Updated from global requirements * Introduce firewall l2 driver base * Updated from global requirements * Fix mismatch in error messages * Use shim tool for ostestr * Updated from global requirements * Updated from global requirements * Remove unused constant for topics * Extend FWaaS V2 ICMP reachability test scenarios * Remove vestigate HUDSON\_PUBLISH\_DOCS reference * devstack: Use entrypoint name for service\_plugin * Use neutron-lib FirewallInternalDriverError * Fix router update on L3 agent restart * Remove log translations * Updated from global requirements * Drop MANIFEST.in - it's not needed by pbr * Updated from global requirements * Fix filtering parameter to get firewall\_group * Update policy on rule addition/removal * [DevStack] Configure iptables\_v2 firewall driver for FWaaS V2 * Updated from global requirements * FW rule applied incorrectly if port specified is a range * Imported Translations from Zanata * Add firewall\_policy\_id in FWaaS v2 * Get default client config from config module * FirewallGroupPortInvalidProject can be raised now * Don't return 404 when deleting a non-existant FWG * consume load\_class\_by\_alias\_or\_classname from neutron-lib * fix releasenotes build * Update reno for stable/pike * Trivial-fix: Omit 'tests/\*' in coverage result * Update links in README 11.0.0.0rc1 ----------- * Updated from global requirements 11.0.0.0b3 ---------- * Change import orders with hacking guide * Add additional test case for Action * Use API Definitions from neutron-lib * Replace deprecated test.attr with decorators.attr * Enable some off-by-default checks * Replace uuid.uuid4() with uuidutils.generate\_uuid() * Update the documentation link for doc migration * Handle rehome of firewall exceptions * Add auto-generated config reference * Updated from global requirements * Fix genconfig in neutron-fwaas * Move remaining policy rules specific to neutron-fwaas * rearrange content to fit the new standard layout * Updated from global requirements * 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 * Replace the usage of 'manager' with 'os\_primary' * Refactor find iptables rules * Updated from global requirements * Add original copyright * use plugin constants from neutron-lib * Updated from global requirements * Trivial-fix: Remove non ascii char 11.0.0.0b2 ---------- * Fixed comments in tox.ini [flake8] * Optimize the link address * Revert "Use neutron-lib definition of neutron-fwaas API" * Modify an order between iptables and conntrack when update firewall * Migrate Neutron's attribute to Neutron-Lib * Use neutron-lib definition of neutron-fwaas API * Fix html\_last\_updated\_fmt for Python3 * Updated from global requirements * Replace assertRaisesRegexp with assertRaisesRegex * Enable to set neutron's local repository for testing * Updated from global requirements * Added neutron\_fwaas.conf file for Firewall config * tox\_install: speed up neutron repo clone * Updated from global requirements * devstack: Adapt to lib/neutron * Updated from global requirements * Improve iptables handling * Updated from global requirements * Remove get\_namespace from API extensions * Updated from global requirements * Add supported conntrack\_driver option to devstack plugin * consume neutron-lib callbacks * Remove unused 'host' parameter from Unit tests * ConntrackNetlink driver to delete conntrack entries * Disable new N537 hacking check from next neutron-lib * Updated from global requirements * Migrate Public attribute to shared in Policy.json * Netlink library to delete conntrack entries 11.0.0.0b1 ---------- * Remove subunit-trace fork * Use \_i18n in unittests * Adding cleanup of router in unit tests * Updated from global requirements * Added 'shared' attribute for firewall resources * Enable to configure conntrack driver * Remove pyroute2.netns.setns patch * Add blank line under the subject * Updated from global requirements * Optimize the link address for fetching git code * Remove unused get\_firewalls\_for\_tenant\_without\_rules * Remove "plugins" directory * Rehome L3 exceptions to neutron-lib * Switch to use stable data\_utils * Updated from global requirements * Revert "Revert "Fix RPC scale issue using cast instead of fanout v1"" * Convert gate\_hook to devstack-tools * Revert "Fix RPC scale issue using cast instead of fanout v1" * Fix RPC scale issue using cast instead of fanout v1 * Use neutron-lib's context module instead of neutron * Updated from global requirements * consume ServicePluginBase from neutron-lib * Fix scale issue in case of services\_sync\_needed v1 * Optimize get\_firewalls\_for\_tenant implementation * Use lazy formatting when logging * [Fix gate]Update test requirement * tempest: Switch to local copy of tempset/scenario/manager.py * Remove duplicate SQL request on firewall update * Updated from global requirements * Updated from global requirements * Define in\_namespace contextmanager * Enable to run rootwrap/privsep related functional tests * Fix some unit test comments * gate\_hook: Stop including localrc * Cleanup setUp method in Unit tests * Remove "constants" duplicated import * Revert "Netlink solution to improve FWaaS performance" * Optimize fwaas tempest api tests * Remove support for py34 * Cleanup useful code in unit tests * Updated from global requirements * Switch to use test\_utils.call\_until\_true * Fix typo in .pylintrc file * Do not complain in firewall\_group\_deleted if the FW is already deleted * Add functional test to run dsvm-functional * Do not complain in firewall\_deleted if the FW is already deleted * Correct LOG message in fwaas\_plugin\_v2 * Update reno for stable/ocata 10.0.0.0rc1 ----------- * Use addCleanup instead of tearDown * Switch to decorators.idempotent\_id * TrivialFix: Added link for modindex * Optimize \_make\_firewall\_group\_dict\_with\_rules 10.0.0.0b3 ---------- * Update policy.json for FWaaS v2 * Updated from global requirements * Netlink solution to improve FWaaS performance * Fix validation in converting 'protocol' * Fix tempest gate issue on FirewallNotFound exception * Optimize \_make\_firewall\_dict\_with\_rules db queries * Updated from global requirements * Fix 'ports' attribute for firewall\_group * TrivialFix : Fix Port Error Message * Fix AssertionError raised from \_set\_rules\_for\_policy * Use BRANCH constants from neutron\_lib * Fix duplicated python version in classifier * Track neutron-lib migration of agent extensions * Support neutron-lib hacking check H904 * Updated from global requirements * [WIP]Remove no used attribute in FirewallPolicyRuleAssociation * Fix update fwr with ipv6 address * Consistent update with router\_info * Fix a typo * Remove httplib2 useless requirement * Updated from global requirements * Enable coverage report in console output * Check error message returned by the system * Fix PENDING\_UPDATE state when update exist no policy fw\_group with ports * Privsep configuration for neutron-fwaas * Updated from global requirements * Not allow to update icmp fw\_rule with port * Use neutron-lib and fwaas constants instead of neutron 10.0.0.0b2 ---------- * Updated from global requirements * Tempest Scenario tests for FWaaS V2 * Docs: fix Devstack config documentation rendering * Changed the home-page link * Adding validation protocol parameters when updating firewall rules * Updated from global requirements * neutron-lib cleanup in neutron-fwaas * Fix removing rule\_association on updating a policy * Add os-testr to test requirements * Use ExtensionDescriptor from neutron-lib * Use DB field sizes instead of \_MAX\_LEN constants * Remove PLURALS * Show team and repo badges on README * Reenable FWaaS v2 tempest tests * Add a variant of gate hook script * Revert "Set NETWORK\_API\_EXTENSIONS in fwaas devstack plugin" * FWaaS v2 Tempest API tests * Set NETWORK\_API\_EXTENSIONS in fwaas devstack plugin * Use TimeoutException from tempest.lib * Updated from global requirements * Switch to using plugins directory in lieu of neutron manager 10.0.0.0b1 ---------- * Updated from global requirements * Replaces uuid.uuid4 with uuidutils.generate\_uuid() * Add smoke tests to neutron-fwaas * Remove unused code * Add developer documentation to FWaaS * Updated from global requirements * Fix fwaas\_v2 driver internal error when restart l3 agent * Remove last vestiges of oslo-incubator * Updated from global requirements * Remove unnecessary neutron agent config reference * Remove import of neutron.extensions * Updated from global requirements * Add Python 3.5 classifier and venv * Catch the integrityError when update firewall group with non exist port * Updated from global requirements * Migrate references from neutron to neutron-lib * Fix UnboundLocalError during update firewall group * Fix typo in comment * Refactor of \_convert\_fwaas\_to\_iptables\_rule and dependencies * Updated from global requirements * Deal with the '-m protocol' flag in iptables FwAAS v1 and v2 * Enable release notes translation * Fix periodic-neutron-fwaas-pyXX-with-neutron-lib-master jobs * Updated from global requirements * Updated from global requirements * Remove reference to third-party drivers in readme * Fix import path for l3\_config * Updated from global requirements * Add devstack plugin support for fwaas v2 * Fix an argument for an exception message * gate\_hook: Fix INSTALL\_MYSQL\_ONLY != True case * Fix KeyError when fw rule associated with a policy is updated * Updated from global requirements * Check for \_interfaces in updated\_router * Add neutron-fwaas in-tree documentation * Update reno for stable/newton 9.0.0.0rc1 ---------- * Tag the alembic migration revisions for Newton * Fix neutron-fwaas tests after project\_id addition * Updated from global requirements * Skip test test\_update\_firewall\_shared\_fails\_for\_non\_admin * Add reno note for FWaaS v2 * Updated from global requirements 9.0.0.0b3 --------- * Add tests ensuring models and migrations are in sync * FWaaS v2 utilize L3 Agent Extension framework * Migrate FWaaS policy.json to FWaaS repo * Use neutron-lib model\_base instead of neutron models * Add FWaaS v1 and v2 entrypoints to setup.cfg * Add special handling for functional tests * Remove vendor driver: vyatta from community repo * Use temporary directory for neutron install * Remove Cisco driver from neutron-fwaas repo * Updated from global requirements * FWaaS v2 Database rule insert/remove operations support * Remove vendor driver: vArmour from community repo * FWaaS v2 L3 Agent Extension * FWaaS V2 Plugin * Fix enum usage in db migration for postgresql * Fix db vs migration mismatches * Fix db migration chain * Fix model/migration sync issues with FWaaS * Updated from global requirements * Constrain remaining tox targets * FWaaS v2 Database * TrivialFix: Add validation for tenant\_id * FwaaS v2 REST API * Fix column\_name in migration from project\_id change * Fix db migration after project\_id changes * Remove temporary local HasProject * Enable DeprecationWarning in test environments * Updating imports as l3 agent config options * Updated from global requirements * devstack: Don't bother to have our own l3 agent config file * Update imports (common.config -> conf.common) * Rename DB columns: tenant -> project * add "reject" action to firewall rule doesn't work for postgresql * Updated from global requirements 9.0.0.0b2 --------- * Delete mcafee FwaaS driver * Updated from global requirements * DevStack plugin for fwaas * Add python 3 classifiers * Add entrypoints for iptables and varmor drivers * Fix deprecation warnings * Fix subunit trace help * Fix a few test cases in test\_cisco\_fwaas\_plugin * Follow the recent tempest change * Remove unused POT file * Updated from global requirements * Remove check\_i18n files * Use call\_and\_ignore\_notfound\_exc directly * Updated from global requirements 9.0.0.0b1 --------- * Fix neutron\_lib deprecations * [Trivial] Remove unnecessary executable privilege * Remove unnecessary executable permissions * Updated from global requirements * Fix broken tempest tests * Skip broken tests * Switch to using hacking checks from neutron-lib * Fix "Not applying Firewall rules immediately" problem 8.1.2 ----- * Updated from global requirements * Updated from global requirements * Don't use zuul-cloner for venv env, for periodic jobs * Fix doc build if git is absent * Updated from global requirements 8.0.0 ----- * Constraint requirements using mitaka upper-constraints.txt file * FWaaS Disable nonstandard-exception due to neutron\_lib shims * Update reno for stable/mitaka 8.0.0.0rc2 ---------- * Update .gitreview for stable/mitaka 8.0.0.0rc1 ---------- * Translations: add in the locale directory * Use routers client instead of networks client * Use tempest.lib base module for network * Updated from global requirements * Make all tox targets constrained * Tag the alembic migration revisions for Mitaka 8.0.0.0b3 --------- * FWaaS: make use of neutron\_lib constants * Use tempest.lib instead of tempest-lib * Track alembic heads * Remove unused pngmath Sphinx extension * Fixed typo in subunit-trace.py * Updated from global requirements * Select the right DB to store user and data in each backend * Modify tox.ini to run dsvm-functional tests * Updated from global requirements * FWaaS: make use of neutron\_lib exceptions * delete meaningless braces in log * Updated from global requirements * Cleanup i18n module usages * tempest: Confirm the opposite direction * tempest: Fix a few test cases * FWaaS quota registration * Add testresources to neutron-fwaas test requirements * Fix neutron-fwaas cover tests * Send Notifications for Firewall policy updates * Set testenv env for api and scenario testing * tempest: Skips router insertion tests if public\_router\_id is configured * tempest: Update for NetworkClient * Add an explicit BRANCH\_NAME to tox\_install.sh * Update translation setup * tempest: Add a comment * Updated from global requirements * tempest: Remove an extra comma, which breaks the following assertion * Add unittest of convert\_action\_to\_case\_insensitive * Updated from global requirements 8.0.0.0b2 --------- * Clean up removed hacking rule from [flake8] ignore lists * tempest: Add test cases for router insertion * Replace deprecated library function os.popen() with subprocess * Add unit test for convert\_protocol * Updated from global requirements * Change LOG.warn to LOG.warning * LOG.warn -> LOG.warning * Trival: Remove unused logging import * tempest: Update after the recent tempest change * Updated from global requirements * Avoid duplicating tenant check when creating resources * Fix a typo from UPPER\_CONTRAINTS\_FILE to UPPER\_CONSTRAINTS\_FILE * Setup for translation * Deprecated tox -downloadcache option removed * Remove Neutron FWaaS static example configuration files * Updated from global requirements * Added constraints tox targets * Automatically generate neutron FWaaS configuration files * Adapt to the recent tempest * Delete python bytecode before every test run * Updated from global requirements * Remove dependency on neutron for topics * Add reno for release notes management * Fix db error when running python34 Unit tests * Remove version from setup.cfg * Validation of router\_ids insertion with column selection 8.0.0.0b1 --------- * Fix TempestPlugin to fix gate failure * Freescale FWaaS Plugin code final decomposition * Updated from global requirements * Add some more tempest scenarios * Updated from global requirements * Set ZUUL\_BRANCH using BRANCH if not available * Move api tests from Neutron repository * Revert "Register FWaaS resources to quota engine" * Remove deprecated use\_namespaces option * Prepare neutron "use\_namespaces" option removal * Update list of modules supporting py34 tests * FWaaS fix comment typos * Updated from global requirements * Updated from global requirements * Add tempest scenario tests * Updated from global requirements * Removed new=True argument from create\_connection * Include alembic versions directory to the package * Register FWaaS resources to quota engine * Updated from global requirements * Validate src\_ip\_adress, dest\_ip\_address and ip\_version * Updated from global requirements * Use assert(Not)In instead of assertTrue/False * use assertIs(Not)None instead of assert(Not)Equal * use assertFalse instead of assertEqual(False, \*\*\*) * Fix argument order for assertEqual to (expected, observed) * Case-Sensitivity for name column in fw table 7.0.0 ----- * Tag the alembic migration revisions for Liberty * Tag the alembic migration revisions for Liberty * Consume FWaaS plugin queue in RPC workers * Kill HEADS file * Stop doing any magic cloning of neutron during CI * Use stable/liberty branch for neutron dep 7.0.0.0rc1 ---------- * Update defaultbranch in .gitreview to stable/liberty * Open Mitaka development * Change ignore-errors to ignore\_errors * Updated from global requirements * Fix check\_migration error * Added +x permission to gate hook scripts * py34 support for fwaas 7.0.0.0b3 --------- * Updated from global requirements * Updated from global requirements * Removing unused dependency: discover * Kill contention between update and delete * FWaaS: Fix 500 INTERNAL error on too long name or description * Adopt the migration chaining and branch names from neutron * Switch to using os-testr's copy of subunit2html * Cisco CSR FWaaS driver should use 'l4-options' in CSR RESTAPI call * Updated from global requirements * Add REJECT rule on Neutron FWaaS * Make tests/unit/extensions discoverable * Updated from global requirements * Reduce contention between update and delete * Add oslotest dependency to test-requirements * Updated from global requirements * Validate tenant\_id between firewall and firewall-policy * Do not make delete\_db\_firewall conditional * Updated from global requirements * switch to multi branch migration tree for liberty * py34: Enable initial python34 testing for FWaaS * Killed existing downgrade rules in migration scripts 7.0.0.0b2 --------- * script.py.mako: update to latest standards * py34: Fix usage of gettext.install * Register alembic\_migrations at install time * Updated from global requirements * Remove quantum untracked files from .gitignore * Fix wrong argument name in \_rpc\_update\_firewall * Use oslo\_log.helpers.log\_method\_call * Updated from global requirements * Fix UT failures during firewall mock plugin setup * COMMON\_PREFIXES cleanup - patch 3/5 * Updated from global requirements * Switch to oslo.service 7.0.0.0b1 --------- * Updated from global requirements * Switch to oslo\_utils.uuidutils * Fixes firewall going to error state on an update * Trim some unused test packages * Update version for Liberty * Updated from global requirements * Add handle for protocol value "any" in CSR1kv driver 7.0.0a0 ------- * Updated from global requirements * Revert "Insert tenant\_id validation for fw and fw-policy" * Updated from global requirements * Python 3: use six.iteritems instead of dict.items * Enable random hash seeds * Do not assume jsonutils.dumps ordering * Updated from global requirements * Add validation of port\_range for firewall-rule * Remove contextlib.nested from tests * vendor code should refer to Brocade * Updated from global requirements * Make iptables firewall work with L3 plugin without DVR support * Updated from global requirements * Insert tenant\_id validation for fw and fw-policy 2015.1.0 -------- * Insert validation in creating/updating firewall * update .gitreview for stable/kilo * Updated Protocol named constants * Add Kilo release milestone * Add Kilo release milestone * Pin neutron to stable/kilo * Restore Brocade Vyatta firewall functionality * FWaaS: Remove check for bash usage * Restore Brocade Vyatta firewall functionality * Switch from neutron.common.log.log to oslo\_log.helpers.log\_method\_call * logic error in updating audited of firewall policy 2015.1.0rc1 ----------- * FWaaS: Reorganize unit test tree * Open Liberty development * Updated Protocol named constants * FWaaS: Refactor callback mechanism * Use BaseSudoTestCase instead of BaseLinuxTestCase * Remove reference to testlib\_plugin * Updated from global requirements 2015.1.0b3 ---------- * Adding mandatory parameters for Router Info in vArmour fwaas agent * tests: stop overwriting neutron BaseTestCase configuration files * Refactoring related to 'FWaaS insertion Model on Routers' * Adds FWaaS driver for Cisco CSR1kv * Adds FWaaS service plugin for Cisco CSR1kv * FWaaS Insertion Model on Routers * Decouple L3 and Firewall during DVR router migration * Implementation of Mcafee NGFW Driver * Provide hooks for FWaaS repo functional gate * Updated from global requirements * Fix for breaking UTs when creating RouterInfo * Add the missing parameter agent\_mode to the FWaaSDriverBase class methods * Add the prefix networking\_brocade to import paths * Pick up missing neutron to neuton\_fwaas change * Removed lockutils-wrapper from tox targets * Migrate to oslo.log * Change L3 agent AdvancedService class to be non-singleton * Updated from global requirements * Enable removing references to self.services\_sync in l3 agent * Stop storing and passing root\_helper * Fix pylint error due to novaclient v1\_1/v2 rename * Add index on tenant\_id * FWaaS: Enable coverage testing * Remove unused \_destroy\_router\_namespaces() * Implementation of Brocade Vyatta Firewall driver * Pass root\_helper to ip\_lib by keyword argument to prep for removal * Fixed tests to use neutron\_fwaas extensions and neutrons * oslo: migrate to namespace-less import paths 2015.1.0b2 ---------- * Code cleanup for Freescale FWaaS Plugin * Updated from global requirements * Move extensions to service repo * Fix neutron-fwaas unit test failures due to multiple issues * Move '\_make\_firewall\_dict\_with\_rules' to firewall\_db.py * Freescale FWaaS Plugin * Updated from global requirements * Updated from global requirements * Updated from global requirements * Migrate to oslo.concurrency * Adjust fwaas unit tests to work with quotas enabled * Updated from global requirements * Update hacking to 0.10 * Fix the neutron-fwaas unit test failures * Updated from global requirements * FWaaS: L3 Agent restructure - observer hierarchy * Cleaned up requirements.txt * Bump from global requirements * Fix gitignore of egg files properly * Do not list neutron in requirements.txt * Re-enable UT for neutron-fwaas for services split * Backward compatibility for fwaas * Update documentation files for FWaaS 2015.1.0b1 ---------- * Kill oslo-incubator files * Move classes out of l3\_agent.py * Delete accidentally committed egg files * Fix python path to neutron\_fwaas module * Init of separate alembic chain * Fix python path in ini file * After the services split, get neutron-fwaas Jenkins jobs passing * Fix git review target for repo * Split fwaas services code into neutron-fwaas * Workflow documentation is now in infra-manual * tox.ini: Prevent casual addition of bash dependency * Updated from global requirements * Get rid of py26 references: OrderedDict, httplib, xml testing * Updated the README.rst * pretty\_tox.sh: Portablity improvement * test\_dhcp\_agent: Fix no-op tests * Enable undefined-loop-variable pylint check * Fix incorrect exception order in \_execute\_request * Migrate to oslo.i18n * Migrate to oslo.middleware * Migrate to oslo.utils * Remove Python 2.6 classifier * Remove ryu plugin * Updated from global requirements * Fix AttributeError in RPC code for DVR * Show progress output while running unit tests * Enforce log hints in neutron.services.firewall * enable H401 hacking check * enable H237 check * Updated from global requirements * Drop RpcProxy usage from FWaaS code * Drop several uses of RpcCallback * Enable default SNAT from networks connected to a router indirectly * Updated from global requirements * Update i18n translation for neutron.db log msg's * Update i18n translation for neutron.agents log msg's * enable F812 check for flake8 * enable F811 check for flake8 * Support pudb as a different post mortem debugger * switch to oslo.serialization * Add rootwrap filters for ofagent * Remove useless return * Remove openvswitch core plugin entry point * Updated from global requirements * Purge use of "PRED and A or B" poor-mans-ternary * Remove duplicate ensure\_remove\_chain method in iptables\_manager * Remove use\_namespaces from RouterInfo Property * Updated from global requirements * Remove XML support * enable F402 check for flake8 * enable E713 in pep8 tests * Hyper-V: Remove useless use of "else" clause on for loop * Enable no-name-in-module pylint check * Updated from global requirements * Remove duplicate import of constants module * Switch run-time import to using importutils.import\_module * Enable assignment-from-no-return pylint check * tox.ini: Avoid using bash where unnecessary * Empty files should not contain copyright or license * Remove single occurrence of lost-exception warning * Updated fileutils and its dependencies * remove E251 exemption from pep8 check * mock.assert\_called\_once() is not a valid method * Add pylint tox environment and disable all existing warnings * Updated from global requirements * Ignore top-level hidden dirs/files by default * Avoid constructing a RouterInfo object to get namespace name * Drop sslutils and versionutils modules * Removed kombu from requirements * Updated from global requirements * Updated from global requirements * Remove sslutils from openstack.common * remove linuxbridge plugin * Open Kilo development * Implement ModelsMigrationsSync test from oslo.db * Disallow unsharing used firewall policy * Fix entrypoint of OneConvergencePlugin plugin * Stop admin using other tenants unshared rules * Set dsvm-functional job to use system packages * Separate Configuration from Freescale SDN ML2 mechanism Driver * Remove @author(s) from copyright statements * Add HA support to the l3 agent * Updated from global requirements * Adds ipset support for Security Groups * Remove useless check in \_rpc\_update\_firewall * Don't allow user to set firewall rule with port and no protocol * Changes to support FWaaS in a DVR based environment * Add requests\_mock to test-requirements.txt * Removed kombu from requirements * Supply missing cisco\_cfg\_agent.ini file * Remove unused arg to config.setup\_logging() * Updated from global requirements * shared policy shouldn't have unshared rules * Work toward Python 3.4 support and testing * Revert "Cisco DFA ML2 Mechanism Driver" * Remove SELECT FOR UPDATE use in delete\_firewall * 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 * Fix state\_path in tests * Remove ovs dependency in embrane plugin * Use lockutils module for tox functional env * Remove SELECT FOR UPDATE use in update\_firewall * Prefer "val !=/== ref" over "val (not) in [ref]" in conditions * Remove status initialization from plugin's create\_firewall * Set firewall state to CREATED when dealing with DVR * Updated from global requirements * Add specific docs build option to tox * Fix bigswitch setup.cfg lines * Remove auto-generation of db schema from models at startup * Skip FWaaS config mismatch check if RPC method is unsupported * Updated from global requirements * Use jsonutils instead of stdlib json * Remove INACTIVE status from FWaaS * Preserve link local IP allocations for DVR fip ns across restart * Opencontrail plug-in implementation for core resources * Return 403 instead of 404 on attr policy failures * Proper validation for inserting firewall rule * Remove redundant topic from rpc calls * Add a tox test environment for random hashseed testing * Updated from global requirements * Move from Python logging to Openstack logging * Remove reference to cisco\_cfg\_agent.ini from setup.cfg * Exit Firewall Agent if config is invalid * Removed configobj from test requirements * Updated from global requirements * Functional tests work fine with random PYTHONHASHSEED * Set python hash seed to 0 in tox.ini * Configuration agent for Cisco devices * Updated from global requirements * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 2 * Audited attribute for policy update not changing * This patch changes the name of directory from mech\_arista to arista * ML2 mechanism driver for SR-IOV capable NIC based switching, Part 1 * Allow to import \_LC, \_LE, \_LI and \_LW functions directly * Make readme reference git.openstack.org not github * Bump hacking to version 0.9.2 * Use auth\_token from keystonemiddleware * Change all occurences of no\_delete to do\_delete * Extract CommonDBMixin to a separate file * Remove reference to setuptools\_git * Add a gate-specific tox env for functional tests * Add CONTRIBUTING.rst * Updated from global requirements * Updated from global requirements * Updated from global requirements * Fix example for running individual tests * Switch to using of oslo.db * remove unsupported middleware * Add config for performance gate job * Synced log module and its dependencies from olso-incubator * don't ignore rules that are already enforced * Moved rpc\_compat.py code back into rpc.py * Updated from global requirements * Updated from global requirements * ofagent: move main module from ryu repository * Remove the useless vim modelines * Removed 'rpc' and 'notifier' incubator modules * Removed create\_rpc\_dispatcher methods * Use openstack.common.lockutils module for locks in tox functional tests * Renamed consume\_in\_thread -> consume\_in\_threads * Port to oslo.messaging * Updated from global requirements * Ignore emacs checkpoint files * Check port value when creating firewall rule with icmp protocol * Configure agents using neutron.common.config.init (formerly .parse) * Added missing core\_plugins symbolic names * Introduced rpc\_compat.create\_connection() * Introduce RpcCallback class * remove pep8 E122 exemption and correct style * remove E112 hacking exemption and fix errors * Updated from global requirements * Fix race condition with firewall deletion * Monkey patch threading module as early as possible * Added RpcProxy class * Freescale SDN Mechanism Driver for ML2 Plugin * Remove run-time version checking for openvswitch features * Log firewall status on delete in case of status inconsistency * Added missing plugin .ini files to setup.cfg * FWaaS plugin doesn't need to handle firewall rule del ops * Updated from global requirements * Disallow regular user to update firewall's shared attribute * Synced jsonutils from oslo-incubator * Cisco APIC ML2 mechanism driver, part 2 * NSX: get rid of the last Nicira/NVP bits * Do not defer IPTables apply in firewall path * Fix non-existent 'assert' calls to mocks * Add missing translation support * Add mailmap entry * Updated from global requirements * Remove explicit dependency on amqplib * Remove duplicate module-rgx line in .pylintrc * Fix H302 violations * Fix H302 violations in unit tests * Updated from global requirements * Updated from global requirements * Exclude .ropeproject from flake8 checks * Remove mock.patch.stop from tests that inherit from BaseTestCase * Enable flake8 E711 and E712 checking * Updated from global requirements * Sync service and systemd modules from oslo-incubator * Move bash whitelisting to pep8 testenv * Fix Jenkins translation jobs * Set ns\_name in RouterInfo as attribute * ignore build directory for pep8 * Return 409 for second firewall creation * Enable hacking H301 check * Updated from global requirements * Remove last parts of Quantum compatibility shim * UT: do not hide an original error in test resource ctxtmgr * Open Juno development * Start using oslosphinx theme for docs * Updated from global requirements * add HEAD sentinel file that contains migration revision * Ensure to count firewalls in target tenant * Mock agent RPC for FWaaS tests to delete DB objs * Fix KeyError except on router\_info in FW Agent * Bugfix and refactoring for ovs\_lib flow methods * Removes calls to mock.patch.stopall in unit tests * Updated from global requirements * Updated from global requirements * Updated from global requirements * One Convergence Neutron Plugin l3 ext support * One Convergence Neutron Plugin Implementation * BigSwitch: Add SSL Certificate Validation * Updated from global requirements * 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 * Updated from global requirements * Remove unused variable * Change firewall to DOWN when admin state down * Update License Headers to replace Nicira with VMware * Developer documentation * options: consolidate options definitions * Rename Neutron core/service plugins for VMware NSX * Updated from global requirements * Sync minimum requirements * Copy cache package from oslo-incubator * ipt\_mgr.ipv6 written in the wrong ipt\_mgr.ipv4 * Validate rule uuids provided for update\_policy * Remove dependent module py3kcompat * Add migration support from agent to NSX dhcp/metadata services * Remove psutil dependency * LBaaS: move agent based driver files into a separate dir * mailmap: update .mailmap * Return request-id in API response * 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 * Add an explicit tox job for functional tests * Base ML2 bulk support on the loaded drivers * Enable hacking H233 rule * Update RPC code from oslo * Configure plugins by name * Update lockutils and fixture in openstack.common * Rename nicira configuration elements to match new naming structure * Remove unused imports * Rename check\_nvp\_config utility tool * Corrects broken format strings in check\_i18n.py * Remove FWaaS Noop driver as default and move to unit tests dir * Updates tox.ini to use new features * Updated from global requirements * Sync global requirements to pin sphinx to sphinx>=1.1.2,<1.2 * Remove start index 0 in range() * Add fwaas\_driver.ini to setup.cfg * Add vpnaas and debug filters to setup.cfg * Updates .gitignore * Update Zhenguo Niu's mailmap * Fwaas can't run in operating system without namespace feature * Replace stubout with fixtures * Ensure get\_pid\_to\_kill works with rootwrap script * Call \_destroy\_metadata\_proxy from \_destroy\_router\_namespaces * Apply six for metaclass * Updated from global requirements * Cleanup HACKING.rst * Fix import log\_handler error with publish\_errors set * Updated from global requirements * Utilizes assertIsNone and assertIsNotNone * Updated from global requirements * Fix incorrect indentations found by Pep 1.4.6+ * avoid changing the rule's own action * Cleanup and make HACKING.rst DRYer * Add support for managing async processes * Remove obsolete redhat-eventlet.patch * Open Icehouse development * Updated from global requirements * Fix to enable delete of firewall in PENDING\_CREATE state * Require oslo.config 1.2.0 final * Fix FWaaS plugin to allow one firewall per tenant * Use built-in print() instead of print statement * FWaaS - fix reordering of rules in policy * Support for NVP advanced FwaaS service * FWaaS - fix policy association of firewall rule * Add l2 population base classes * Fix message i18n error * Install metering\_agent.ini and vpn\_agent.ini * fix conversion type missing * vArmour gateway agent and FWaaS driver * Enclose command args in with\_venv.sh * ML2 Mechanism Driver for Cisco Nexus * Reference driver implementation (IPsec) for VPNaaS * Implement ML2 port binding * Adding more unit tests for the FWaaS agent * Arista ML2 Mechanism driver * ML2 Mechanism Driver for Tail-f Network Control System (NCS) * Default to not capturing log output in tests * Add Neutron l3 metering agent * Update mailmap * Fix wrong example in HACKING.rst * Bumps hacking to 0.7.0 * remove binaries under bin * Fixes Windows setup dependency bug * Restore Babel to requirements.txt * fix up inadevertant octal to make hacking pass * Firewall as a Service (FWaaS) Iptables Driver * Remove DHCP lease logic * Firewall as a Service (FWaaS) Agent * Remove last vestiges of nose * Updated from global requirements * Ignore pbr\*.egg directory * Fix H102, H103 Apache 2.0 license hacking check error * Remove openstack.common.exception usage * Adds Babel dependency missing from 555d27c * Fix the alphabetical order in requirement files * Followup fixes to FWaaS API patch * Remove comments from requirements.txt (workaround pbr bug) * Remove last of assertEquals * Firewall as a Service (FWaaS) APIs and DB Model * remove netifaces dependency of ryu-agent * Add gre tunneling support for the ML2 plugin * Add VXLAN tunneling support for the ML2 plugin * xenapi - rename quantum to neutron * Fix issue with pip installing oslo.config-1.2.0 * Initial Modular L2 Mechanism Driver implementation * Add cover/ to .gitignore * fix some missing change from quantum to neutron * git remove old non-working packaging files * Rename Quantum to Neutron * Rename quantum to neutron in .gitreview * Sync install\_venv\_common from oslo * Update to use OSLO db * Require greenlet 0.3.2 (or later) * Remove single-version-externally-managed in setup.cfg * Fix single-version-externally-mananged typo in setup.cfg * Allow use of lowercase section names in conf files * Require pbr 0.5.16 or newer * Update to the latest stevedore * Rename agent\_loadbalancer directory to loadbalancer * Remove unit tests that are no longer run * Update with latest OSLO code * Remove explicit distribute depend * Fix and enable H90x tests * Remove generic Exception when using assertRaises * Add \*.swo/swp to .gitignore * python3: Introduce py33 to tox.ini * Rename README to README.rst * Rename requires files to standard names * Initial Modular L2 plugin implementation * Revert dependency on oslo.config 1.2.0 * Perform a sync with oslo-incubator * Require oslo.config 1.2.0a2 * update mailmap * Revert "Fix ./run\_tests.sh --pep8" * Move to pbr * Docstrings formatted according to pep257 * relax amqplib and kombu version requirements * Fix ./run\_tests.sh --pep8 * blueprint mellanox-quantum-plugin * Update flake8 pinned versions * Let the cover venv run individual tests * Copy the RHEL6 eventlet workaround from Oslo * Remove locals() from strings substitutions * Enable automatic validation of many HACKING rules * Shorten the path of the nicira nvp plugin * Allow pdb debugging in manually-invoked tests * Reformat openstack-common.conf * Switch to flake8 from pep8 * Parallelize quantum unit testing: * blueprint cisco-single-config * Add lbaas\_agent files to setup.py * Add VIRTUAL\_ENV key to enviroment passed to patch\_tox\_env * Pin SQLAlchemy to 0.7.x * Sync latest Oslo components for updated copyright * drop rfc.sh * Replace "OpenStack LLC" with "OpenStack Foundation" * First havana commit * remove references to netstack in setup.py * Switch to final 1.1.0 oslo.config release * Update to Quantum Client 2.2.0 * Update tox.ini to support RHEL 6.x * Switch to oslo.config * Add common test base class to hold common things * Pin pep8 to 1.3.3 * Add initial testr support * LBaaS Agent Reference Implementation * Bump python-quantumclient version to 2.1.2 * Add scheduling feature basing on agent management extension * Remove compat cfg wrapper * Unpin PasteDeploy dependency version * Use testtools instead of unittest or unittest2 * Add midonet to setup.py * Sync latest install\_venv\_common.py with olso * Add check-nvp-config utility * Add unit test for ryu-agent * Use oslo-config-2013.1b3 * Adds Brocade Plugin implementation * Synchronize code from oslo * PLUMgrid quantum plugin * Update .coveragerc * Allow tools/install\_venv\_common.py to be run from within the source directory * Updated to latest oslo-version code * Use install\_venv\_common.py from oslo * Cisco plugin cleanup * Use babel to generate translation file * Update WebOb version to >=1.2 * Update latest OSLO * Adding multi switch support to the Cisco Nexus plugin * Adds support for deploying Quantum on Windows * Latest OSLO updates * Port to argparse based cfg * Add migration support to Quantum * Undo change to require WebOb 1.2.3, instead, require only >=1.0.8 * .gitignore cleanup * Upgrade WebOb to 1.2.3 * Logging module cleanup * Add OVS cleanup utility * Add tox artifacts to .gitignore * Add restproxy.ini to config\_path in setup.py * Add script for checking i18n message * l3 agent rpc * Add metadata\_agent.ini to config\_path in setup.py * Remove \_\_init\_\_.py from bin/ and tools/ * add metadata proxy support for Quantum Networks * Use auth\_token middleware in keystoneclient * Add QUANTUM\_ prefix for env used by quantum-debug * Make tox.ini run pep8 checks on bin * Explicitly include versioninfo in tarball * Import lockutils and fileutils from openstack-common * Updated openstack-common setup and version code * Ensure that the anyjson version is correct * Add eventlet\_backdoor and threadgroup from openstack-common * Add loopingcall from openstack-common * Added service from openstack-common * Drop lxml dependency * Add uuidutils module * Import order clean-up * pin sqlalchemy to 0.7 * Correct Intended Audience * Add OpenStack trove classifier for PyPI * Improve unit test times * l3\_nat\_agent was renamed to l3\_agent and this was missed * Support for several HA RabbitMQ servers * add missing files from setup.py * Create .mailmap file * Lower webob dep from v1.2.0 to v1.0.8 * Implements agent for Quantum Networking testing * Create utility to clean-up netns * Update rootwrap; track changes in nova/cinder * Execute unit tests for Cisco plugin with Quantum tests * Add lease expiration script support for dnsmasq * Add nosehtmloutput as a test dependency * quantum l3 + floating IP support * Updates pip requirements * NEC OpenFlow plugin support * remove old gflags config code * RPC support for OVS Plugin and Agent * Initial implemention of MetaPlugin * RPC support for Linux Bridge Plugin and Agent * Exempt openstack-common from pep8 check * fix bug lp:1025526,update iniparser.py to accept empty value * Introduce files from openstack common * fix bug lp:1019230,update rpc from openstack-common * implement dhcp agent for quantum * Use setuptools git plugin for file inclusion * Remove paste configuration details to a seperate file. blueprint use-common-cfg * Implements the blueprint use-common-cfg for the quantum service. More specifically uses global CONF for the quantum.conf file * Add authZ through incorporation of policy checks * Bug #1013967 - Quantum is breaking on tests with pep 1.3 * Use openstack.common.exception * API v2: mprove validation of post/put, rename few attributes * Add API v2 support * Fix up test running to match jenkins expectation * Add build\_sphinx options * Quantum should use openstack.common.jsonutils * Remove hardcoded version for pep8 from tools/test-requires * Quantum should use openstack.common.importutils * PEP8 fixes * Bug #1002605 * Parse linuxbridge plugins using openstack.common.cfg * Add HACKING.rst to tarball generation bug 1001220 * Include AUTHORS in release package * Change Resource.\_\_call\_\_() to not leak internal errors * Removed simplejson from pip-requires * Remove dependency on python-quantumclient * Add sphinx to the test build deps * Add HACKING.rst coding style doc * bug 963152: add a few missing files to sdist tarball * Fix path to python-quantumclient * Split out pip requires and aligned tox file * Fix missing files in sdist package [bug 954906] * Downgraded required version of WebOb to 1.0.8 * more files missing in sdist tarball * make sure pip-requires is included in setup.py sdist * remove pep8 and strict lxml version from setup.py * plugin: introduce ryu plugin * bug 934459: pip no longer supports -E * blueprint quantum-ovs-tunnel-agent * Initial commit: nvp plugin * Cleanup the source distribution * blueprint quantum-linux-bridge-plugin * Remove quantum CLI console script * Bug 925372: remove deprecated webob attributes (and also specify stable webob version in pip-requires) * Make tox config work * Pin versions to standard versions * Split out quantum.client and quantum.common * Quantum was missing depend on lxml * moving batch config out of quantum-server repo * Getting ready for the client split * Removed erroneous print from setup.py * Base version.py on glance * Fix lp bug 897882 * Install a good version of pip in the venv * Rename .quantum-venv to .venv * Remove plugin pip-requires * Bug #890028 * Fix for bug 900316 * Second round of packaging changes * Changes to make pip-based tests work with jenkins * Fix for bug 888811 * Fix for Bug #888820 - pip-requires file support for plugins * blueprint quantum-packaging * Add .gitreview config file for gerrit * Add code-coverage support to run\_tests.sh (lp860160) 2011.3 ------ * Add rfc.sh to help with gerrit workflow * merge tyler's unit tests for cisco plugin changes lp845140 * merge salv's no-cheetah CLI branch lp 842190 * merge sumit's branch for lp837752 * Merging latest from lp:quantum * Merging lo:~salvatore-orlando/quantum/quantum-api-auth * Updating CLI for not using Cheetah anymore. Now using a mechanism based on Python built-in templates * Merging Sumit's changes including fixes for multinic support, and CLI module for working with extensions * Merging from Cisco branch * Merging from lp:quantum * merge cisco consolidated plugin changes * Merging lp:~salvatore-orlando/quantum/bug834449 * merge trunk * Merging from lp:quantum * merge salvatore's new cli code * Addressing comments from Dan * Merging from quantum * merge cisco extensions branch * Merging from Sumit's branch, changes to VIF-driver and Scheduler; extension action names have been changed in response to Salvatore's review comments in the extensions branch review * Syncing with Cisco extensions branch * Merging from Sumit's branch, import ordering related changes * Merging the Cisco branch * Finishing cli work Fixing bug with XML deserialization * Merging lp:~salvatore-orlando/quantum/quantum-api-alignment * merge latest quantum branch and resolve conflicts * Merging lp:~asomya/quantum/lp833163 Fix for Bug #833163: Pep8 violations in recent packaging changes that were merged into trunk (Critical) * PEP8 fixes for setup.py * Merging lp:~cisco-openstack/quantum/802dot1qbh-vifdriver-scheduler * Merging lp:~cisco-openstack/quantum/l2network-plugin-persistence * Merging lp:quantum * merging with lp:quantum * Making Keystone version configurable * Merging branch: lp:~danwent/quantum/test-refactor * Syncing with lp:quantum * Merging fixes and changes batch-config script. Thanks lp:danwent ! * Merging lp:~asomya/quantum/lp824145 Fix for Bug#824145 : Adding a setup script for quantum * merge trunk pep8 fixes adapting CLI to API v1.0 Fixing wsgi to avoid failure with extensions * merge trunk * Pulling in changes from lp:quantum * Merging Cisco's contribution to Quantum. Thanks to various folks at Cisco Systems, Quantum will have plugins to integrate with Cisco UCS blade servers using 802.1Qbh, Cisco Nexus family of switches and the ability for Quantum plugin to have multiple switches/devices within a single Quantum plugin * Merging from Sumit's branch pylint fixes and incorporating review comments * Mergin from cisco brach * Merging from lp:quantum * Introducting cheetah Updating list\_nets in CLI Writing unit tests for list\_nets Stubbing out with FakeConnection now * Merging quantum extenions framework into trunk. Thanks rajaram vinkesh, deepak & santhosh for the great work! * lp Bug#824145 : Adding a setup script for quantum * skeleton for cli unit tests * merge trunk * Merged quantum trunk * - Adding setup script * force batch\_config.py to use json, as XML has issues (see bug: 798262) * update batch\_config.py to use new client lib, hooray for deleting code * Merging changes addressing Bug # 802772. Thanks lp:danwent ! * Merging bugfix for Bug 822890 - Added License file for Quantum code distribution * L2 Network Plugin Framework merge * Adding Apache Version 2.0 license file. This is the official license agreement under which Quantum code is available to the Open Source community * merge * merge heckj's pip-requires fixes * updates to pip-requires for CI * Merged quantum trunk * Merging changes from lp:quantum * Completing API spec alignment Unit tests aligned with changes in the API spec * Merging the brand new Quantum-client-library feature * Merging lp:quantum updates * persistence of l2network & ucs plugins using mysql - db\_conn.ini - configuration details of making a connection to the database - db\_test\_plugin.py - contains abstraction methods for storing database values in a dict and unit test cases for DB testing - l2network\_db.py - db methods for l2network models - l2network\_models.py - class definitions for the l2 network tables - ucs\_db.py - db methods for ucs models - ucs\_models.py - class definition for the ucs tables dynamic loading of the 2nd layer plugin db's based on passed arguments Create, Delete, Get, Getall, Update database methods at - Quantum, L2Network and Ucs Unit test cases for create, delete, getall and update operations for L2Network and Ucs plugins pep8 checks done branch based off revision 34 plugin-framework * Merged from trunk * merged the latest changes from plugin-framework branch - revision 39 conforming to the new cisco plugin directory structure and moving all db related modules into cisco/db folder updated db\_test\_plugin.py - added import of cisco constants module - added LOG.getLogger for logging component name - updated import module paths for l2network\_models/db and ucs\_models/db to use the new directory structure - updated (rearranged) imports section to obey openstack alphabetical placement convention updated db\_conn.ini - updated database name from cisco\_naas to quantum\_l2network unit test cases ran successfully and pep8 checks done again * merge branch for to fix bug817826 * Merging the latest changes from lp:quantum * fix bug 817826 and similar error in batch\_config.py * merge Salvatore's api branch with fixes for tests. Tweaking branch to remove unwanted bin/quantum.py as part of merge * Santhosh/Rajaram|latest merge from quantum and made extensions use options to load plugin * Apply fix for bug #797419 merging lp:~salvatore-orlando/quantum/bug797419 * Merging branch lp:~netstack/quantum/quantum-unit-tests * Merged from quantum trunk * Adapated plugin infrastructure to allow API to pass options to plugins Now using in-memory sqlite db for tests on FakePlugin teardown() now 'resets' the in-memory db Adding unit tests for APIs * Adding Routes>=1.12.3 to tools/pip-requires * Merging dan wendlandt's bugfixes for Bug #800466 and improvements that enable Quantum to seamlessly run on KVM! * more pep8 goodness * refactor batch\_config, allow multiple attaches with the empty string * merge and pep8 cleanup * Merging latest changes from parent repo - lp:network-service , Parent repo had approved merge proposal for merging lp:~santhom/network-service/quantum\_testing\_framework , which has now been merged into lp:network-service * Merging pep8 and functional test related changes lp:~santhom/network-service/quantum\_testing\_framework branch * add example to usage string for batch\_config.py * Bug fixes and clean-up, including supporting libvirt * Santhosh/Vinkesh | Added the testing framework. Moved the smoketest to tests/functional * Pushing initial started code based on Glance project and infrstructure work done by the melange team * Merging in latest changes from lp:quantum ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/HACKING.rst0000664000175000017500000000036300000000000016205 0ustar00zuulzuul00000000000000Neutron FWaaS Style Commandments ================================ Please see the Neutron HACKING.rst file for style commandments for neutron-fwaas: `Neutron HACKING.rst `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/LICENSE0000664000175000017500000002363700000000000015425 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. ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.341352 neutron-fwaas-20.0.0/PKG-INFO0000664000175000017500000000410700000000000015504 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: neutron-fwaas Version: 20.0.0 Summary: OpenStack Networking FWaaS Home-page: https://docs.openstack.org/neutron-fwaas/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Welcome! ======== This package contains the code for the Neutron Firewall as a Service (FWaaS) service. This package requires Neutron to run. External 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 git.openstack.org at: . Please refer to Neutron documentation for more information: `Neutron README.rst `_ Get release notes: `Neutron FWaaS Release Notes `_ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/README.rst0000664000175000017500000000171000000000000016073 0ustar00zuulzuul00000000000000======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Welcome! ======== This package contains the code for the Neutron Firewall as a Service (FWaaS) service. This package requires Neutron to run. External 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 git.openstack.org at: . Please refer to Neutron documentation for more information: `Neutron README.rst `_ Get release notes: `Neutron FWaaS Release Notes `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/TESTING.rst0000664000175000017500000000076400000000000016263 0ustar00zuulzuul00000000000000Testing Neutron FWaaS ===================== 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-fwaas as well: `Neutron TESTING.rst `_ For instructions on how to use FWaaS with devstack, look at: `Neutron-FWaaS DevStack `_ ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/babel.cfg0000664000175000017500000000002100000000000016124 0ustar00zuulzuul00000000000000[python: **.py] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/bindep.txt0000664000175000017500000000061700000000000016413 0ustar00zuulzuul00000000000000# This file contains runtime (non-python) dependencies # More info at: http://docs.openstack.org/infra/bindep/readme.html # MySQL and PostgreSQL databases since some jobs are set up in # OpenStack infra that need these like libpq-dev [test] libczmq4 [test] # Packages required e.g. in functional tests libnetfilter-log1 [platform:dpkg platform:suse] libnetfilter-log [platform:rpm !platform:suse] ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/devstack/0000775000175000017500000000000000000000000016211 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/devstack/README.rst0000664000175000017500000000173700000000000017710 0ustar00zuulzuul00000000000000========================= neutron-fwaas in DevStack ========================= This is setup as a DevStack plugin. For more information on DevStack plugins, see the `DevStack Plugins documentation `_. Please note that the old 'q-fwaas' keyword still exists, You can specify enable_service q-fwaas or enable_service q-fwaas-v2 in local.conf How to run FWaaS V2 in DevStack =============================== Add the following to the localrc section of your local.conf to configure FWaaS v2. .. code-block:: ini [[local|localrc]] enable_plugin neutron-fwaas https://git.openstack.org/openstack/neutron-fwaas To check a specific patchset that is currently under development, use a form like the below example, which is checking out change 214350 patch set 14 for testing. .. code-block:: ini [[local|localrc]] enable_plugin neutron-fwaas https://review.openstack.org/p/openstack/neutron-fwaas refs/changes/50/214350/14 ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/devstack/lib/0000775000175000017500000000000000000000000016757 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/devstack/lib/l2_agent0000664000175000017500000000106400000000000020376 0ustar00zuulzuul00000000000000# This file was shamelessly stolen from the neutron repository here: # https://opendev.org/openstack/neutron/src/branch/master/devstack/lib/l2_agent function plugin_agent_add_l2_agent_extension { local l2_agent_extension=$1 if [[ -z "$L2_AGENT_EXTENSIONS" ]]; then L2_AGENT_EXTENSIONS=$l2_agent_extension elif [[ ! ,${L2_AGENT_EXTENSIONS}, =~ ,${l2_agent_extension}, ]]; then L2_AGENT_EXTENSIONS+=",$l2_agent_extension" fi } function configure_l2_agent { iniset /$Q_PLUGIN_CONF_FILE agent extensions "$L2_AGENT_EXTENSIONS" } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/devstack/lib/l3_agent0000664000175000017500000000106100000000000020374 0ustar00zuulzuul00000000000000# This file is completely based on one in the neutron repository here: # https://opendev.org/openstack/neutron/src/branch/master/devstack/lib/l2_agent function plugin_agent_add_l3_agent_extension { local l3_agent_extension=$1 if [[ -z "$L3_AGENT_EXTENSIONS" ]]; then L3_AGENT_EXTENSIONS=$l3_agent_extension elif [[ ! ,${L3_AGENT_EXTENSIONS}, =~ ,${l3_agent_extension}, ]]; then L3_AGENT_EXTENSIONS+=",$l3_agent_extension" fi } function configure_l3_agent { iniset $Q_L3_CONF_FILE agent extensions "$L3_AGENT_EXTENSIONS" } ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/devstack/plugin.sh0000775000175000017500000001035100000000000020046 0ustar00zuulzuul00000000000000#!/bin/bash # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # Dependencies: # # ``functions`` file # ``DEST`` must be defined # Save trace setting XTRACE=$(set +o | grep xtrace) set +o xtrace # Source in L2 and L3 agent extension management LIBDIR=$DEST/neutron-fwaas/devstack/lib source $LIBDIR/l2_agent source $LIBDIR/l3_agent function install_fwaas() { # Install the service. : setup_develop $DEST/neutron-fwaas if is_ubuntu; then install_package libnetfilter-log1 else # EPEL install_package libnetfilter_log fi } function configure_fwaas_v2() { # Add conf file cp $NEUTRON_FWAAS_DIR/etc/neutron_fwaas.conf.sample $NEUTRON_FWAAS_CONF neutron_server_config_add $NEUTRON_FWAAS_CONF inicomment $NEUTRON_FWAAS_CONF service_providers service_provider iniadd $NEUTRON_FWAAS_CONF service_providers service_provider $NEUTRON_FWAAS_SERVICE_PROVIDERV2 neutron_fwaas_configure_driver fwaas_v2 if is_service_enabled q-l3; then iniset_multiline $Q_L3_CONF_FILE fwaas agent_version v2 iniset_multiline $Q_L3_CONF_FILE fwaas driver $FWAAS_DRIVER_V2 fi if is_service_enabled q-agt; then iniset /$NEUTRON_CORE_PLUGIN_CONF fwaas firewall_l2_driver $FW_L2_DRIVER iniset /$NEUTRON_CORE_PLUGIN_CONF agent extensions fwaas_v2 fi } function configure_l3_log_fwaas_v2(){ if is_service_enabled q-l3; then iniadd $Q_L3_CONF_FILE agent extensions fwaas_v2_log fi } function neutron_fwaas_generate_config_files { (cd $NEUTRON_FWAAS_DIR && exec ./tools/generate_config_file_samples.sh) } function init_fwaas() { # Initialize and start the service. : # Using sudo to gain the root privilege to be able to copy file to rootwrap.d sudo cp $DEST/neutron-fwaas/etc/neutron/rootwrap.d/fwaas-privsep.filters /etc/neutron/rootwrap.d/fwaas-privsep.filters } function shutdown_fwaas() { # Shut the service down. : } function cleanup_fwaas() { # Cleanup the service. : if is_ubuntu; then uninstall_package libnetfilter-log1 else # EPEL uninstall_package libnetfilter_log fi } function neutron_fwaas_configure_common { neutron_service_plugin_class_add $FWAAS_PLUGIN_V2 } function neutron_fwaas_configure_driver { if is_service_enabled q-l3; then plugin_agent_add_l3_agent_extension $1 configure_l3_agent iniset_multiline $Q_L3_CONF_FILE fwaas enabled True fi } # check for service enabled if is_service_enabled q-svc neutron-api && is_service_enabled q-fwaas q-fwaas-v2 neutron-fwaas-v2; then if [[ "$1" == "stack" && "$2" == "install" ]]; then # Perform installation of service source echo_summary "Installing neutron-fwaas" install_fwaas elif [[ "$1" == "stack" && "$2" == "post-config" ]]; then # Configure after the other layer 1 and 2 services have been configured neutron_fwaas_configure_common neutron_fwaas_generate_config_files echo_summary "Configuring neutron-fwaas for FWaaS v2" configure_fwaas_v2 if is_service_enabled q-log neutron-log; then echo_summary "Configuring FwaaS V2 packet log for l3 extension" configure_l3_log_fwaas_v2 fi elif [[ "$1" == "stack" && "$2" == "extra" ]]; then # Initialize and start the neutron-fwaas service echo_summary "Initializing neutron-fwaas" init_fwaas fi if [[ "$1" == "unstack" ]]; then # Shut down neutron-fwaas services # no-op shutdown_fwaas fi if [[ "$1" == "clean" ]]; then # Remove state and transient data # Remember clean.sh first calls unstack.sh # no-op cleanup_fwaas fi fi # Restore xtrace $XTRACE ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/devstack/settings0000664000175000017500000000074200000000000017777 0ustar00zuulzuul00000000000000FWAAS_DRIVER_V2=${FWAAS_DRIVER_V2:-iptables_v2} FW_L2_DRIVER=${FW_L2_DRIVER:-noop} FWAAS_PLUGIN_V2=${FWAAS_PLUGIN:-firewall_v2} NEUTRON_FWAAS_DIR=$DEST/neutron-fwaas NEUTRON_FWAAS_CONF_FILE=neutron_fwaas.conf NEUTRON_FWAAS_CONF=$NEUTRON_CONF_DIR/$NEUTRON_FWAAS_CONF_FILE NEUTRON_FWAAS_SERVICE_PROVIDERV2=${NEUTRON_FWAAS_SERVICE_PROVIDERV2:-FIREWALL_V2:fwaas_db:neutron_fwaas.services.firewall.service_drivers.agents.agents.FirewallAgentDriver:default} enable_service q-fwaas-v2 ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/doc/0000775000175000017500000000000000000000000015152 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/requirements.txt0000664000175000017500000000053200000000000020436 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. sphinx!=1.6.6,!=1.6.7,>=1.6.2 # BSD sphinxcontrib-apidoc>=0.2.0 # BSD openstackdocstheme>=1.18.1 # Apache-2.0 reno>=2.5.0 # Apache-2.0 ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/doc/source/0000775000175000017500000000000000000000000016452 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/doc/source/_static/0000775000175000017500000000000000000000000020100 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/_static/.placeholder0000664000175000017500000000000000000000000022351 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/conf.py0000664000175000017500000002357500000000000017765 0ustar00zuulzuul00000000000000# -*- coding: utf-8 -*- # Copyright (c) 2010 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. # # Keystone documentation build configuration file, created by # sphinx-quickstart on Tue May 18 13:50:15 2010. # # This file is execfile()'d with the current directory set to it's 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. import os import sys # 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. BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ROOT_DIR = os.path.abspath(os.path.join(BASE_DIR, "..", "..")) sys.path.insert(0, ROOT_DIR) # -- 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 = ['sphinxcontrib.apidoc', 'sphinx.ext.coverage', 'sphinx.ext.ifconfig', 'sphinx.ext.graphviz', 'sphinx.ext.todo', 'oslo_config.sphinxext', 'oslo_config.sphinxconfiggen', 'oslo_policy.sphinxext', 'oslo_policy.sphinxpolicygen', 'openstackdocstheme',] try: import openstackdocstheme extensions.append('openstackdocstheme') except ImportError: openstackdocstheme = None todo_include_todos = True # sphinxcontrib.apidoc options apidoc_module_dir = '../../neutron_fwaas' apidoc_output_dir = 'contributor/api' # TODO(hoangcx): remove 'services/logapi/*' and # 'services/firewall/fwaas_plugin_v2.py' after the next neutron release # (current release is Rocky-3) # NOTE(longkb): Due to libnetfilter_log library is not installed in sphinx-docs # gate, so we would like to ignore 'privileged/netfilter_log/*'. apidoc_excluded_paths = [ 'db/migration/alembic_migrations/*', 'privileged/netfilter_log/*', 'services/firewall/fwaas_plugin_v2.py', 'services/logapi/*', 'setup.py', 'tests/*', 'tests'] apidoc_separate_modules = True # Add any paths that contain templates here, relative to this directory. templates_path = [] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8' # The master toctree document. master_doc = 'index' # General information about the project. project = 'Neutron FWaaS' copyright = '2011-present, OpenStack Foundation.' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # Version info from neutron_fwaas.version import version_info as neutron_fwaas_version release = neutron_fwaas_version.release_string() # The short X.Y version. version = neutron_fwaas_version.version_string() # 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 documents that shouldn't be included in the build. # unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = [] # The reST default role (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 = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. modindex_common_prefix = ['neutron_fwaas.'] # -- Options for man page output -------------------------------------------- # Grouping the document tree for man pages. # List of tuples 'sourcefile', 'target', 'title', 'Authors name', 'manual' #man_pages = [ # ('man/neutron-server', 'neutron-server', 'Neutron Server', # ['OpenStack'], 1) #] # -- 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' if openstackdocstheme is not None: html_theme = 'openstackdocs' else: html_theme = 'default' # 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 = ['_theme'] # 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'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' html_last_updated_fmt = '%Y-%m-%d %H:%M' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_use_modindex = 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, 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 = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = '' # Output file base name for HTML help builder. #htmlhelp_basename = 'neutrondoc' # -- Options for LaTeX output ------------------------------------------------ # The paper size ('letter' or 'a4'). #latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). #latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ ('index', 'doc-neutron-fwaas.tex', 'Neutron Firewall-as-s-Service Documentation', 'Neutron development team', '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 # Additional stuff for the LaTeX preamble. #latex_preamble = '' # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_use_modindex = 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}', } # -- Options for openstackdocstheme ------------------------------------------- repository_name = 'openstack/neutron-fwaas' bug_project = 'neutron' bug_tag = 'doc' # -- Options for oslo_config.sphinxconfiggen --------------------------------- _config_generator_config_files = [ 'fwaas_driver.ini', 'neutron_fwaas.conf', ] 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-fwaas' ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/doc/source/configuration/0000775000175000017500000000000000000000000021321 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/fwaas_driver.rst0000664000175000017500000000020200000000000024521 0ustar00zuulzuul00000000000000================ fwaas_driver.ini ================ .. show-options:: :config-file: etc/oslo-config-generator/fwaas_driver.ini ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/index.rst0000664000175000017500000000150500000000000023163 0ustar00zuulzuul00000000000000.. _configuring: ================================= Neutron FWaaS Configuration Guide ================================= This section provides a list of all possible options for each configuration file. Configuration ------------- Neutron FWaaS uses the following configuration files for its various services. .. toctree:: :maxdepth: 1 neutron_fwaas fwaas_driver The following are sample configuration files for Neutron FWaaS and utilities. These are generated from code and reflect the current state of code in the neutron-fwaas repository. .. toctree:: :glob: :maxdepth: 1 samples/* Policy ------ Neutron FWaaS, 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=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/neutron_fwaas.rst0000664000175000017500000000021200000000000024721 0ustar00zuulzuul00000000000000================== neutron_fwaas.conf ================== .. show-options:: :config-file: etc/oslo-config-generator/neutron_fwaas.conf ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/policy-sample.rst0000664000175000017500000000105400000000000024631 0ustar00zuulzuul00000000000000================================ Sample Neutron FWaaS Policy File ================================ The following is a sample neutron-fwaas 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-fwaas when this documentation is built. You must ensure your version of neutron-fwaas matches the version of this documentation. .. literalinclude:: /_static/neutron-fwaas.policy.yaml.sample ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/policy.rst0000664000175000017500000000045200000000000023353 0ustar00zuulzuul00000000000000====================== neutron-fwaas policies ====================== The following is an overview of all available policies in neutron-fwaas. For a sample configuration file, refer to :doc:`/configuration/policy-sample`. .. show-policy:: :config-file: etc/oslo-policy-generator/policy.conf ././@PaxHeader0000000000000000000000000000003200000000000011450 xustar000000000000000026 mtime=1712145114.28535 neutron-fwaas-20.0.0/doc/source/configuration/samples/0000775000175000017500000000000000000000000022765 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/samples/fwaas_driver.rst0000664000175000017500000000041700000000000026175 0ustar00zuulzuul00000000000000======================= Sample fwaas_driver.ini ======================= This sample configuration can also be viewed in `the raw format <../../_static/config_samples/fwaas_driver.conf.sample>`_. .. literalinclude:: ../../_static/config_samples/fwaas_driver.conf.sample ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/configuration/samples/neutron_fwaas.rst0000664000175000017500000000042700000000000026375 0ustar00zuulzuul00000000000000========================= Sample neutron_fwaas.conf ========================= This sample configuration can also be viewed in `the raw format <../../_static/config_samples/neutron_fwaas.conf.sample>`_. .. literalinclude:: ../../_static/config_samples/neutron_fwaas.conf.sample ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/doc/source/contributor/0000775000175000017500000000000000000000000021024 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/contributor/contributing.rst0000664000175000017500000000216500000000000024271 0ustar00zuulzuul00000000000000============================= Contributing to neutron-fwaas ============================= If you would like to contribute to the development of OpenStack, you must follow the steps documented at: https://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: https://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad in the 'neutron' project: https://bugs.launchpad.net/neutron To get in touch with the neutron-fwaas community, look at the following resources: - Join the #openstack-fwaas IRC channel on Freenode. This is where the FireWall-as-a-Service team is available for discussion. - Join the `FireWall-as-a-Service weekly IRC meeting `_ where the status of new initiatives and bugs is discussed. These are a great places to get recommendations on where to start contributing to neutron-fwaas. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/contributor/devstack.rst0000664000175000017500000000005200000000000023357 0ustar00zuulzuul00000000000000.. include:: ../../../devstack/README.rst ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/contributor/fwaas_v2.rst0000664000175000017500000000042700000000000023271 0ustar00zuulzuul00000000000000FireWall as a Service V2 ======================== The `FireWall as a Service API V2 `_ specification lists the changes that together compose FWaaS V2. These changes are not fully implemented. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/contributor/index.rst0000664000175000017500000000037700000000000022674 0ustar00zuulzuul00000000000000================= Contributor Guide ================= .. toctree:: :maxdepth: 2 contributing fwaas_v2 devstack .. API reference contains a lot of sections, toctree with maxdepth 1 is used. .. toctree:: :glob: :maxdepth: 1 modules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/contributor/modules.rst0000664000175000017500000000066200000000000023232 0ustar00zuulzuul00000000000000================ Module Reference ================ .. The module reference is rendered in HTML version much much better. PDF version is not good for reading due to page width, lack of TOC in subsections and so on, so we skip the module reference in PDF version. .. only:: html .. toctree:: :maxdepth: 1 :glob: api/* .. only:: latex See the online version of this document for the module reference. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/index.rst0000664000175000017500000000125200000000000020313 0ustar00zuulzuul00000000000000=========================== neutron-fwaas documentation =========================== .. warning:: Due to lack of maintainers this project is now deprecated in the Neutron stadium and will be removed from stadium in ``W`` cycle. If You want to step in and be maintainer of this project to keep it in the Neutron stadium, please contact the ``neutron team`` via openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron @freenode. .. toctree:: :glob: :maxdepth: 2 install/index configuration/index contributor/index .. only:: html .. rubric:: Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/doc/source/install/0000775000175000017500000000000000000000000020120 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/doc/source/install/index.rst0000664000175000017500000000260700000000000021766 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. 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-fwaas Or, if you have virtualenvwrapper installed:: $ mkvirtualenv neutron-fwaas $ pip install neutron-fwaas For information on what to do with FWaaS once it is installed, please check the Networking Guide `Firewall-as-a-Service (FWaaS) v2 scenario `_ or the `Firewall-as-a-Service (FWaaS) v1 scenario `_. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/etc/0000775000175000017500000000000000000000000015160 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/etc/README.txt0000664000175000017500000000047500000000000016664 0ustar00zuulzuul00000000000000To generate the sample neutron-fwaas configuration files, run the following command from the top level of the neutron-fwaas directory: tox -e genconfig If a 'tox' environment is unavailable, then you can run the following script instead to generate the configuration files: ./tools/generate_config_file_samples.sh ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2653494 neutron-fwaas-20.0.0/etc/neutron/0000775000175000017500000000000000000000000016652 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/etc/neutron/rootwrap.d/0000775000175000017500000000000000000000000020751 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/etc/neutron/rootwrap.d/fwaas-privsep.filters0000664000175000017500000000041200000000000025127 0ustar00zuulzuul00000000000000# neutron-fwaas privsep filters # This file should be owned by (and only-writeable by) the root user [Filters] privsep-rootwrap: PathFilter, privsep-helper, root, privsep-helper, --config-file, /etc/(?!\.\.).*, --privsep_context, neutron_fwaas.privileged.default ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/etc/oslo-config-generator/0000775000175000017500000000000000000000000021363 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/etc/oslo-config-generator/fwaas_driver.ini0000664000175000017500000000014000000000000024533 0ustar00zuulzuul00000000000000[DEFAULT] output_file = etc/fwaas_driver.ini.sample wrap_width = 79 namespace = firewall.agent ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/etc/oslo-config-generator/neutron_fwaas.conf0000664000175000017500000000014200000000000025102 0ustar00zuulzuul00000000000000[DEFAULT] output_file = etc/neutron_fwaas.conf.sample wrap_width = 79 namespace = neutron.fwaas ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/etc/oslo-policy-generator/0000775000175000017500000000000000000000000021415 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/etc/oslo-policy-generator/policy.conf0000664000175000017500000000011100000000000023554 0ustar00zuulzuul00000000000000[DEFAULT] output_file = etc/policy.yaml.sample namespace = neutron-fwaas ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/neutron_fwaas/0000775000175000017500000000000000000000000017260 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/__init__.py0000664000175000017500000000125000000000000021367 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=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/_i18n.py0000664000175000017500000000203700000000000020552 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_fwaas" _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 def get_available_languages(): return oslo_i18n.get_available_languages(DOMAIN) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/cmd/0000775000175000017500000000000000000000000020023 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/cmd/__init__.py0000664000175000017500000000000000000000000022122 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/cmd/upgrade_checks/0000775000175000017500000000000000000000000022772 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/cmd/upgrade_checks/__init__.py0000664000175000017500000000000000000000000025071 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/cmd/upgrade_checks/checks.py0000664000175000017500000000260600000000000024610 0ustar00zuulzuul00000000000000# Copyright 2019 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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.utils import upgrade_checks as base_checks from oslo_config import cfg from oslo_upgradecheck import upgradecheck from neutron_fwaas._i18n import _ class Checks(base_checks.BaseChecks): def get_checks(self): return [ (_("Check FWaaS v1"), self.fwaas_v1_check) ] @staticmethod def fwaas_v1_check(checker): fwaas_v1_names = [ 'firewall', 'neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin'] for name in fwaas_v1_names: if name in cfg.CONF.service_plugins: return upgradecheck.Result( upgradecheck.Code.FAILURE, _("FWaaS v1 is removed. " "FWaaS v2 should be used instead.")) return upgradecheck.Result(upgradecheck.Code.SUCCESS) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/cmd/v1_to_v2_db_migration.py0000664000175000017500000001312300000000000024552 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.common import config from neutron.db import models_v2 from oslo_config import cfg from oslo_db.sqlalchemy import enginefacade from oslo_log import log as logging from neutron_fwaas._i18n import _ from neutron_fwaas.db.firewall import firewall_db as firewall_db_v1 from neutron_fwaas.db.firewall.v2 import firewall_db_v2 LOG = logging.getLogger(__name__) def setup_conf(): cli_opts = [ cfg.StrOpt('neutron-db-connection', required=True, help=_('neutron database connection string')), ] conf = cfg.CONF conf.register_cli_opts(cli_opts) conf() def migrate_fwaas_v1_to_v2(db_session): # the entire migration process will be done under the same transaction to # allow full rollback in case of error with db_session.begin(subtransactions=True): # Read all V1 policies v1_policies = db_session.query(firewall_db_v1.FirewallPolicy) for v1_pol in v1_policies: LOG.info("Migrating FWaaS V1 policy %s", v1_pol.id) # read the rules of this policy v1_rules = db_session.query(firewall_db_v1.FirewallRule).filter_by( firewall_policy_id=v1_pol.id).all() # Create the V2 policy v2_pol = firewall_db_v2.FirewallPolicy( id=v1_pol.id, tenant_id=v1_pol.tenant_id, name=v1_pol.name, description=v1_pol.description, shared=v1_pol.shared, audited=v1_pol.audited, rule_count=len(v1_rules)) db_session.add(v2_pol) # Add the rules and associate them with the policy for v1_rule in v1_rules: LOG.info("Migrating FWaaS V1 rule %s", v1_rule.id) v2_rule = firewall_db_v2.FirewallRuleV2( id=v1_rule.id, name=v1_rule.name, description=v1_rule.description, tenant_id=v1_rule.tenant_id, shared=v1_rule.shared, protocol=v1_rule.protocol, ip_version=v1_rule.ip_version, source_ip_address=v1_rule.source_ip_address, destination_ip_address=v1_rule.destination_ip_address, source_port_range_min=v1_rule.source_port_range_min, source_port_range_max=v1_rule.source_port_range_max, destination_port_range_min=( v1_rule.destination_port_range_min), destination_port_range_max=( v1_rule.destination_port_range_max), action=v1_rule.action, enabled=v1_rule.enabled) db_session.add(v2_rule) v2_link = firewall_db_v2.FirewallPolicyRuleAssociation( firewall_policy_id=v1_pol.id, firewall_rule_id=v1_rule.id, position=v1_rule.position) db_session.add(v2_link) # Read all V1 firewalls v1_fws = db_session.query(firewall_db_v1.Firewall) for v1_fw in v1_fws: LOG.info("Migrating FWaaS V1 firewall %s", v1_fw.id) # create the V2 firewall group v2_fw_group = firewall_db_v2.FirewallGroup( id=v1_fw.id, name=v1_fw.name, description=v1_fw.description, tenant_id=v1_fw.tenant_id, shared=v1_fw.shared, admin_state_up=v1_fw.admin_state_up, status=v1_fw.status, ingress_firewall_policy_id=v1_fw.firewall_policy_id, egress_firewall_policy_id=v1_fw.firewall_policy_id) db_session.add(v2_fw_group) # for every router in the V1 Firewall router association, add all # its interface ports to the V2 FirewallGroupPortAssociation v1_routers = db_session.query( firewall_db_v1.FirewallRouterAssociation).filter_by( fw_id=v1_fw.id) for v1_router in v1_routers: rtr_id = v1_router.router_id LOG.info("Migrating FWaaS V1 %s router %s", v1_fw.id, rtr_id) if_ports = db_session.query(models_v2.Port).filter_by( device_id=rtr_id, device_owner="network:router_interface") for port in if_ports: fw_port = firewall_db_v2.FirewallGroupPortAssociation( firewall_group_id=v2_fw_group.id, port_id=port.id) db_session.add(fw_port) def main(): # Initialize the cli options setup_conf() config.setup_logging() # Get the neutron DB session neutron_context_manager = enginefacade.transaction_context() neutron_context_manager.configure( connection=cfg.CONF.neutron_db_connection) n_session_maker = neutron_context_manager.writer.get_sessionmaker() n_session = n_session_maker(autocommit=True) # Run DB migration migrate_fwaas_v1_to_v2(n_session) LOG.info("DB migration done.") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/common/0000775000175000017500000000000000000000000020550 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/common/__init__.py0000664000175000017500000000000000000000000022647 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/common/exceptions.py0000664000175000017500000000164500000000000023311 0ustar00zuulzuul00000000000000# Copyright 2018 Fujitsu Limited. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions as n_exc from neutron_fwaas._i18n import _ # TODO(annp): migrate to neutron-lib after Queen release class FirewallGroupPortNotSupported(n_exc.Conflict): message = _("Port %(port_id)s is not supported by firewall driver " "'%(driver_name)s'.") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/common/fwaas_constants.py0000664000175000017500000000242500000000000024322 0ustar00zuulzuul00000000000000# Copyright 2015 Cisco Systems, Inc # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. FIREWALL = 'FIREWALL' FIREWALL_V2 = 'FIREWALL_V2' # Constants for "topics" FIREWALL_PLUGIN = 'q-firewall-plugin' FW_AGENT = 'firewall_agent' FIREWALL_RULE_LIST = 'firewall_rule_list' # V2 Constants DEFAULT_FWG = 'default' DEFAULT_FWP_INGRESS = 'default ingress' DEFAULT_FWP_EGRESS = 'default egress' # Firewall group events for agent-side DELETE_FWG = 'delete_firewall_group' UPDATE_FWG = 'update_firewall_group' CREATE_FWG = 'create_firewall_group' # Port events for L2 agent extension HANDLE_PORT = 'handle_port' DELETE_PORT = 'delete_port' # Resource name FIREWALL_GROUP = 'firewall_group' FIREWALL_RULE = 'firewall_rule' FIREWALL_POLICY = 'firewall_policy' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/common/resources.py0000664000175000017500000000140200000000000023131 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_fwaas.db.firewall.v2 import firewall_db_v2 FIREWALL_GROUP = firewall_db_v2.FirewallGroup FIREWALL_POLICY = firewall_db_v2.FirewallPolicy FIREWALL_RULE = firewall_db_v2.FirewallRuleV2 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/db/0000775000175000017500000000000000000000000017645 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/__init__.py0000664000175000017500000000000000000000000021744 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/db/firewall/0000775000175000017500000000000000000000000021452 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/firewall/__init__.py0000664000175000017500000000000000000000000023551 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/firewall/firewall_db.py0000664000175000017500000000705300000000000024303 0ustar00zuulzuul00000000000000# Copyright 2013 Big Switch Networks, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib.db import model_base import sqlalchemy as sa from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy import orm # Note(annp): Keep firewall db v1 structure for migration class FirewallRule(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a Firewall rule.""" __tablename__ = 'firewall_rules' __table_args__ = ({'mysql_collate': 'utf8_bin'}) name = sa.Column(sa.String(255)) description = sa.Column(sa.String(1024)) firewall_policy_id = sa.Column(sa.String(36), sa.ForeignKey('firewall_policies.id'), nullable=True) shared = sa.Column(sa.Boolean) protocol = sa.Column(sa.String(40)) ip_version = sa.Column(sa.Integer, nullable=False) source_ip_address = sa.Column(sa.String(46)) destination_ip_address = sa.Column(sa.String(46)) source_port_range_min = sa.Column(sa.Integer) source_port_range_max = sa.Column(sa.Integer) destination_port_range_min = sa.Column(sa.Integer) destination_port_range_max = sa.Column(sa.Integer) action = sa.Column(sa.Enum('allow', 'deny', 'reject', name='firewallrules_action')) enabled = sa.Column(sa.Boolean) position = sa.Column(sa.Integer) class Firewall(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a Firewall resource.""" __tablename__ = 'firewalls' __table_args__ = ({'mysql_collate': 'utf8_bin'}) name = sa.Column(sa.String(255)) description = sa.Column(sa.String(1024)) shared = sa.Column(sa.Boolean) admin_state_up = sa.Column(sa.Boolean) status = sa.Column(sa.String(16)) firewall_policy_id = sa.Column(sa.String(36), sa.ForeignKey('firewall_policies.id'), nullable=True) class FirewallPolicy(model_base.BASEV2, model_base.HasId, model_base.HasProject): """Represents a Firewall Policy resource.""" __tablename__ = 'firewall_policies' __table_args__ = ({'mysql_collate': 'utf8_bin'}) name = sa.Column(sa.String(255)) description = sa.Column(sa.String(1024)) shared = sa.Column(sa.Boolean) firewall_rules = orm.relationship( FirewallRule, backref=orm.backref('firewall_policies', cascade='all, delete'), order_by='FirewallRule.position', collection_class=ordering_list('position', count_from=1)) audited = sa.Column(sa.Boolean) firewalls = orm.relationship(Firewall, backref='firewall_policies') class FirewallRouterAssociation(model_base.BASEV2): """Tracks FW Router Association""" __tablename__ = 'firewall_router_associations' fw_id = sa.Column(sa.String(36), sa.ForeignKey('firewalls.id', ondelete="CASCADE"), primary_key=True) router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id', ondelete="CASCADE"), primary_key=True) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/db/firewall/v2/0000775000175000017500000000000000000000000022001 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/firewall/v2/__init__.py0000664000175000017500000000000000000000000024100 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/firewall/v2/firewall_db_v2.py0000664000175000017500000014521000000000000025237 0ustar00zuulzuul00000000000000# Copyright (c) 2016 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy import netaddr from neutron_lib import constants as nl_constants from neutron_lib.db import api as db_api from neutron_lib.db import constants as db_constants from neutron_lib.db import model_base from neutron_lib.db import model_query from neutron_lib.db import resource_extend from neutron_lib.db import standard_attr from neutron_lib.db import utils as db_utils from neutron_lib import exceptions from neutron_lib.exceptions import firewall_v2 as f_exc from oslo_config import cfg from oslo_db import exception as db_exc from oslo_log import log as logging from oslo_utils import uuidutils import sqlalchemy as sa from sqlalchemy.ext.orderinglist import ordering_list from sqlalchemy import or_ from sqlalchemy import orm from sqlalchemy.orm import exc from neutron_fwaas.common import fwaas_constants as const LOG = logging.getLogger(__name__) class FirewallDefaultParameterExists(exceptions.InUse): """Default Firewall Parameter conflict exception Occurs when user creates/updates any existing firewall resource with reserved parameter names. """ message = ("Operation cannot be performed since '%(name)s' " "is a reserved name for %(resource_type)s.") class FirewallDefaultObjectUpdateRestricted(FirewallDefaultParameterExists): message = ("Operation cannot be performed on default object " "'%(resource_id)s' of type %(resource_type)s.") class HasName(object): name = sa.Column(sa.String(db_constants.NAME_FIELD_SIZE)) class HasDescription(object): description = sa.Column( sa.String(db_constants.LONG_DESCRIPTION_FIELD_SIZE)) class FirewallRuleV2(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, HasName, model_base.HasProject): __tablename__ = "firewall_rules_v2" shared = sa.Column(sa.Boolean) protocol = sa.Column(sa.String(40)) ip_version = sa.Column(sa.Integer) source_ip_address = sa.Column(sa.String(46)) destination_ip_address = sa.Column(sa.String(46)) source_port_range_min = sa.Column(sa.Integer) source_port_range_max = sa.Column(sa.Integer) destination_port_range_min = sa.Column(sa.Integer) destination_port_range_max = sa.Column(sa.Integer) action = sa.Column(sa.Enum('allow', 'deny', 'reject', name='firewallrules_action')) enabled = sa.Column(sa.Boolean) api_collections = ['firewall_rules'] collection_resource_map = {"firewall_rules": "firewall_rule"} tag_support = True class FirewallGroup(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, HasName, model_base.HasProject): __tablename__ = 'firewall_groups_v2' port_associations = orm.relationship( 'FirewallGroupPortAssociation', backref=orm.backref('firewall_group_port_associations_v2', cascade='all, delete')) name = sa.Column(sa.String(db_constants.NAME_FIELD_SIZE)) ingress_firewall_policy_id = sa.Column( sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_policies_v2.id')) egress_firewall_policy_id = sa.Column( sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_policies_v2.id')) admin_state_up = sa.Column(sa.Boolean) status = sa.Column(sa.String(db_constants.STATUS_FIELD_SIZE)) shared = sa.Column(sa.Boolean) api_collections = ['firewall_groups'] collection_resource_map = {"firewall_groups": "firewall_group"} tag_support = True class DefaultFirewallGroup(model_base.BASEV2, model_base.HasProjectPrimaryKey): __tablename__ = "default_firewall_groups" firewall_group_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_groups_v2.id', ondelete="CASCADE"), nullable=False) firewall_group = orm.relationship( FirewallGroup, lazy='joined', backref=orm.backref('default_firewall_group', cascade='all,delete'), primaryjoin="FirewallGroup.id==DefaultFirewallGroup.firewall_group_id", ) class FirewallGroupPortAssociation(model_base.BASEV2): __tablename__ = 'firewall_group_port_associations_v2' firewall_group_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_groups_v2.id', ondelete="CASCADE"), primary_key=True) port_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('ports.id', ondelete="CASCADE"), unique=True, primary_key=True) class FirewallPolicyRuleAssociation(model_base.BASEV2): """Tracks FW Policy and Rule(s) Association""" __tablename__ = 'firewall_policy_rule_associations_v2' firewall_policy_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_policies_v2.id', ondelete="CASCADE"), primary_key=True) firewall_rule_id = sa.Column(sa.String(db_constants.UUID_FIELD_SIZE), sa.ForeignKey('firewall_rules_v2.id', ondelete="CASCADE"), primary_key=True) position = sa.Column(sa.Integer) class FirewallPolicy(standard_attr.HasStandardAttributes, model_base.BASEV2, model_base.HasId, HasName, model_base.HasProject): __tablename__ = 'firewall_policies_v2' name = sa.Column(sa.String(db_constants.NAME_FIELD_SIZE)) rule_count = sa.Column(sa.Integer) audited = sa.Column(sa.Boolean) rule_associations = orm.relationship( FirewallPolicyRuleAssociation, backref=orm.backref('firewall_policies_v2', cascade='all, delete'), order_by='FirewallPolicyRuleAssociation.position', collection_class=ordering_list('position', count_from=1)) shared = sa.Column(sa.Boolean) api_collections = ['firewall_policies'] collection_resource_map = {"firewall_policies": "firewall_policy"} tag_support = True def _list_firewall_groups_result_filter_hook(query, filters): values = filters and filters.get('ports', []) if values: query = query.join(FirewallGroupPortAssociation) query = query.filter(FirewallGroupPortAssociation.port_id.in_(values)) return query def _list_firewall_policies_result_filter_hook(query, filters): values = filters and filters.get('firewall_rules', []) if values: query = query.join(FirewallPolicyRuleAssociation) query = query.filter( FirewallPolicyRuleAssociation.firewall_rule_id.in_(values)) return query class FirewallPluginDb(object): def __new__(cls, *args, **kwargs): model_query.register_hook( FirewallGroup, "firewall_group_v2_filter_by_port_association", query_hook=None, filter_hook=None, result_filters=_list_firewall_groups_result_filter_hook) model_query.register_hook( FirewallPolicy, "firewall_policy_v2_filter_by_firewall_rule_association", query_hook=None, filter_hook=None, result_filters=_list_firewall_policies_result_filter_hook) return super(FirewallPluginDb, cls).__new__(cls, *args, **kwargs) def _get_firewall_group(self, context, id): try: return model_query.get_by_id(context, FirewallGroup, id) except exc.NoResultFound: raise f_exc.FirewallGroupNotFound(firewall_id=id) def _get_firewall_policy(self, context, id): try: return model_query.get_by_id(context, FirewallPolicy, id) except exc.NoResultFound: raise f_exc.FirewallPolicyNotFound(firewall_policy_id=id) def _get_firewall_rule(self, context, id): try: return model_query.get_by_id(context, FirewallRuleV2, id) except exc.NoResultFound: raise f_exc.FirewallRuleNotFound(firewall_rule_id=id) def _validate_fwr_protocol_parameters(self, fwr): protocol = fwr['protocol'] source_port = fwr['source_port'] dest_port = fwr['destination_port'] if protocol and protocol not in (nl_constants.PROTO_NAME_TCP, nl_constants.PROTO_NAME_UDP): if source_port or dest_port: raise f_exc.FirewallRuleInvalidICMPParameter( param="Source, destination port") if not protocol and (source_port or dest_port): raise f_exc.FirewallRuleWithPortWithoutProtocolInvalid() def _validate_fwr_src_dst_ip_version(self, fwr, fwr_db=None): src_version = dst_version = None if fwr.get('source_ip_address', None): src_version = netaddr.IPNetwork(fwr['source_ip_address']).version if fwr.get('destination_ip_address', None): dst_version = netaddr.IPNetwork( fwr['destination_ip_address']).version rule_ip_version = fwr.get('ip_version', None) if not rule_ip_version and fwr_db: rule_ip_version = fwr_db.ip_version if ((src_version and src_version != rule_ip_version) or (dst_version and dst_version != rule_ip_version)): raise f_exc.FirewallIpAddressConflict() def _validate_fwr_port_range(self, min_port, max_port): if int(min_port) > int(max_port): port_range = '%s:%s' % (min_port, max_port) raise f_exc.FirewallRuleInvalidPortValue(port=port_range) def _get_min_max_ports_from_range(self, port_range): if not port_range: return [None, None] min_port, sep, max_port = port_range.partition(":") if not max_port: max_port = min_port self._validate_fwr_port_range(min_port, max_port) return [int(min_port), int(max_port)] def _get_port_range_from_min_max_ports(self, min_port, max_port): if not min_port: return None if min_port == max_port: return str(min_port) self._validate_fwr_port_range(min_port, max_port) return '%s:%s' % (min_port, max_port) def _make_firewall_rule_dict(self, firewall_rule, fields=None, policies=None): src_port_range = self._get_port_range_from_min_max_ports( firewall_rule['source_port_range_min'], firewall_rule['source_port_range_max']) dst_port_range = self._get_port_range_from_min_max_ports( firewall_rule['destination_port_range_min'], firewall_rule['destination_port_range_max']) res = {'id': firewall_rule['id'], 'tenant_id': firewall_rule['tenant_id'], 'name': firewall_rule['name'], 'description': firewall_rule['description'], 'protocol': firewall_rule['protocol'], 'firewall_policy_id': policies, 'ip_version': firewall_rule['ip_version'], 'source_ip_address': firewall_rule['source_ip_address'], 'destination_ip_address': firewall_rule['destination_ip_address'], 'source_port': src_port_range, 'destination_port': dst_port_range, 'action': firewall_rule['action'], 'enabled': firewall_rule['enabled'], 'shared': firewall_rule['shared']} if hasattr(firewall_rule.standard_attr, 'id'): res['standard_attr_id'] = firewall_rule.standard_attr.id resource_extend.apply_funcs('firewall_rules', res, firewall_rule) return db_utils.resource_fields(res, fields) def _make_firewall_policy_dict(self, firewall_policy, fields=None): fw_rules = [ rule_association.firewall_rule_id for rule_association in firewall_policy['rule_associations']] res = {'id': firewall_policy['id'], 'tenant_id': firewall_policy['tenant_id'], 'name': firewall_policy['name'], 'description': firewall_policy['description'], 'audited': firewall_policy['audited'], 'firewall_rules': fw_rules, 'shared': firewall_policy['shared']} if hasattr(firewall_policy.standard_attr, 'id'): res['standard_attr_id'] = firewall_policy.standard_attr.id resource_extend.apply_funcs('firewall_policies', res, firewall_policy) return db_utils.resource_fields(res, fields) def _make_firewall_group_dict(self, firewall_group_db, fields=None): fwg_ports = [port_assoc.port_id for port_assoc in firewall_group_db.port_associations] res = {'id': firewall_group_db['id'], 'tenant_id': firewall_group_db['tenant_id'], 'name': firewall_group_db['name'], 'description': firewall_group_db['description'], 'ingress_firewall_policy_id': firewall_group_db['ingress_firewall_policy_id'], 'egress_firewall_policy_id': firewall_group_db['egress_firewall_policy_id'], 'admin_state_up': firewall_group_db['admin_state_up'], 'ports': fwg_ports, 'status': firewall_group_db['status'], 'shared': firewall_group_db['shared']} if hasattr(firewall_group_db.standard_attr, 'id'): res['standard_attr_id'] = firewall_group_db.standard_attr.id resource_extend.apply_funcs('firewall_groups', res, firewall_group_db) return db_utils.resource_fields(res, fields) def _get_policy_ordered_rules(self, context, policy_id): query = (context.session.query(FirewallRuleV2) .join(FirewallPolicyRuleAssociation) .filter_by(firewall_policy_id=policy_id) .order_by(FirewallPolicyRuleAssociation.position)) return [self._make_firewall_rule_dict(rule) for rule in query] def make_firewall_group_dict_with_rules(self, context, firewall_group_id): firewall_group = self.get_firewall_group(context, firewall_group_id) ingress_policy_id = firewall_group['ingress_firewall_policy_id'] if ingress_policy_id: firewall_group['ingress_rule_list'] = ( self._get_policy_ordered_rules(context, ingress_policy_id)) else: firewall_group['ingress_rule_list'] = [] egress_policy_id = firewall_group['egress_firewall_policy_id'] if egress_policy_id: firewall_group['egress_rule_list'] = ( self._get_policy_ordered_rules(context, egress_policy_id)) else: firewall_group['egress_rule_list'] = [] return firewall_group def _check_firewall_rule_conflict(self, fwr_db, fwp_db): if not fwr_db['shared']: if fwr_db['tenant_id'] != fwp_db['tenant_id']: raise f_exc.FirewallRuleConflict( firewall_rule_id=fwr_db['id'], project_id=fwr_db['tenant_id']) def _process_rule_for_policy(self, context, firewall_policy_id, firewall_rule_id, position, association_db): with db_api.CONTEXT_READER.using(context): fwp_query = context.session.query( FirewallPolicy).with_for_update() fwp_db = fwp_query.filter_by(id=firewall_policy_id).one() if position: # Note that although position numbering starts at 1, # internal ordering of the list starts at 0, so we compensate. fwp_db.rule_associations.insert( position - 1, FirewallPolicyRuleAssociation( firewall_rule_id=firewall_rule_id)) else: fwp_db.rule_associations.remove(association_db) context.session.delete(association_db) fwp_db.rule_associations.reorder() fwp_db.audited = False return self._make_firewall_policy_dict(fwp_db) def _get_policy_rule_association_query(self, context, firewall_policy_id, firewall_rule_id): fwpra_query = context.session.query(FirewallPolicyRuleAssociation) return fwpra_query.filter_by(firewall_policy_id=firewall_policy_id, firewall_rule_id=firewall_rule_id) def _ensure_rule_not_already_associated(self, context, firewall_policy_id, firewall_rule_id): """Checks that a rule is not already associated with a particular policy. If it is the function will throw an exception. """ try: self._get_policy_rule_association_query( context, firewall_policy_id, firewall_rule_id).one() raise f_exc.FirewallRuleAlreadyAssociated( firewall_rule_id=firewall_rule_id, firewall_policy_id=firewall_policy_id) except exc.NoResultFound: return def _get_policy_rule_association(self, context, firewall_policy_id, firewall_rule_id): """Returns the association between a firewall rule and a firewall policy. Throws an exception if the assocition does not exist. """ try: return self._get_policy_rule_association_query( context, firewall_policy_id, firewall_rule_id).one() except exc.NoResultFound: raise f_exc.FirewallRuleNotAssociatedWithPolicy( firewall_rule_id=firewall_rule_id, firewall_policy_id=firewall_policy_id) def _create_default_firewall_rules(self, context, tenant_id): # NOTE(xgerman) Maybe generating the final set of rules from a # configuration file makes sense. Can be done some time later # 1. Firewall rule for ingress IPv4 packets (DROP by default) in_fwr_v4 = { 'description': 'default ingress rule for IPv4', 'name': 'default ingress ipv4', 'shared': cfg.CONF.default_fwg_rules.shared, 'protocol': cfg.CONF.default_fwg_rules.protocol, 'tenant_id': tenant_id, 'ip_version': nl_constants.IP_VERSION_4, 'action': cfg.CONF.default_fwg_rules.ingress_action, 'enabled': cfg.CONF.default_fwg_rules.enabled, 'source_port': cfg.CONF.default_fwg_rules.ingress_source_port, 'source_ip_address': cfg.CONF.default_fwg_rules.ingress_source_ipv4_address, 'destination_port': cfg.CONF.default_fwg_rules.ingress_destination_port, 'destination_ip_address': cfg.CONF.default_fwg_rules. ingress_destination_ipv4_address, } # 2. Firewall rule for ingress IPv6 packets (DROP by default) in_fwr_v6 = copy.deepcopy(in_fwr_v4) in_fwr_v6['description'] = 'default ingress rule for IPv6' in_fwr_v6['name'] = 'default ingress ipv6' in_fwr_v6['ip_version'] = nl_constants.IP_VERSION_6 in_fwr_v6['source_ip_address'] = \ cfg.CONF.default_fwg_rules.ingress_source_ipv6_address in_fwr_v6['destination_ip_address'] = \ cfg.CONF.default_fwg_rules.ingress_destination_ipv6_address # 3. Firewall rule for egress IPv4 packets (ALLOW by default) eg_fwr_v4 = copy.deepcopy(in_fwr_v4) eg_fwr_v4['description'] = 'default egress rule for IPv4' eg_fwr_v4['name'] = 'default egress ipv4' eg_fwr_v4['action'] = cfg.CONF.default_fwg_rules.egress_action eg_fwr_v4['source_port'] = \ cfg.CONF.default_fwg_rules.egress_source_port eg_fwr_v4['source_ip_address'] = \ cfg.CONF.default_fwg_rules.egress_source_ipv4_address eg_fwr_v4['destination_port'] = \ cfg.CONF.default_fwg_rules.egress_destination_port eg_fwr_v4['destination_ip_address'] = \ cfg.CONF.default_fwg_rules.egress_destination_ipv4_address # 4. Firewall rule for egress IPv6 packets (ALLOW by default) eg_fwr_v6 = copy.deepcopy(in_fwr_v6) eg_fwr_v6['description'] = 'default egress rule for IPv6' eg_fwr_v6['name'] = 'default egress ipv6' eg_fwr_v6['action'] = cfg.CONF.default_fwg_rules.egress_action eg_fwr_v6['source_port'] = \ cfg.CONF.default_fwg_rules.egress_source_port eg_fwr_v6['source_ip_address'] = \ cfg.CONF.default_fwg_rules.egress_source_ipv6_address eg_fwr_v6['destination_port'] = \ cfg.CONF.default_fwg_rules.egress_destination_port eg_fwr_v6['destination_ip_address'] = \ cfg.CONF.default_fwg_rules.egress_destination_ipv6_address return { 'in_ipv4': self.create_firewall_rule(context, in_fwr_v4)['id'], 'in_ipv6': self.create_firewall_rule(context, in_fwr_v6)['id'], 'eg_ipv4': self.create_firewall_rule(context, eg_fwr_v4)['id'], 'eg_ipv6': self.create_firewall_rule(context, eg_fwr_v6)['id'], } def create_firewall_rule(self, context, firewall_rule): fwr = firewall_rule self._validate_fwr_protocol_parameters(fwr) self._validate_fwr_src_dst_ip_version(fwr) src_port_min, src_port_max = self._get_min_max_ports_from_range( fwr['source_port']) dst_port_min, dst_port_max = self._get_min_max_ports_from_range( fwr['destination_port']) with db_api.CONTEXT_WRITER.using(context): fwr_db = FirewallRuleV2( id=uuidutils.generate_uuid(), tenant_id=fwr['tenant_id'], name=fwr['name'], description=fwr['description'], protocol=fwr['protocol'], ip_version=fwr['ip_version'], source_ip_address=fwr['source_ip_address'], destination_ip_address=fwr['destination_ip_address'], source_port_range_min=src_port_min, source_port_range_max=src_port_max, destination_port_range_min=dst_port_min, destination_port_range_max=dst_port_max, action=fwr['action'], enabled=fwr['enabled'], shared=fwr['shared']) context.session.add(fwr_db) return self._make_firewall_rule_dict(fwr_db) def update_firewall_rule(self, context, id, firewall_rule): fwr = firewall_rule fwr_db = self._get_firewall_rule(context, id) fwr_db_updated = self._make_firewall_rule_dict(fwr_db) fwr_db_updated.update(fwr) self._validate_fwr_protocol_parameters(fwr_db_updated) self._validate_fwr_src_dst_ip_version(fwr_db_updated) if 'source_port' in fwr: src_port_min, src_port_max = self._get_min_max_ports_from_range( fwr['source_port']) fwr['source_port_range_min'] = src_port_min fwr['source_port_range_max'] = src_port_max del fwr['source_port'] if 'destination_port' in fwr: dst_port_min, dst_port_max = self._get_min_max_ports_from_range( fwr['destination_port']) fwr['destination_port_range_min'] = dst_port_min fwr['destination_port_range_max'] = dst_port_max del fwr['destination_port'] with db_api.CONTEXT_WRITER.using(context): fwr_db.update(fwr) # if the rule on a policy, fix audited flag fwp_ids = self.get_policies_with_rule(context, id) for fwp_id in fwp_ids: fwp_db = self._get_firewall_policy(context, fwp_id) fwp_db['audited'] = False return self._make_firewall_rule_dict(fwr_db) def delete_firewall_rule(self, context, id): with db_api.CONTEXT_WRITER.using(context): fwr = self._get_firewall_rule(context, id) # make sure rule is not associated with any policy if self.get_policies_with_rule(context, id): raise f_exc.FirewallRuleInUse(firewall_rule_id=id) context.session.delete(fwr) def insert_rule(self, context, id, rule_info): firewall_rule_id = rule_info['firewall_rule_id'] # ensure rule is not already assigned to the policy self._ensure_rule_not_already_associated(context, id, firewall_rule_id) insert_before = True ref_firewall_rule_id = None if 'insert_before' in rule_info: ref_firewall_rule_id = rule_info['insert_before'] if not ref_firewall_rule_id and 'insert_after' in rule_info: # If insert_before is set, we will ignore insert_after. ref_firewall_rule_id = rule_info['insert_after'] insert_before = False with db_api.CONTEXT_WRITER.using(context): fwr_db = self._get_firewall_rule(context, firewall_rule_id) fwp_db = self._get_firewall_policy(context, id) self._check_firewall_rule_conflict(fwr_db, fwp_db) if ref_firewall_rule_id: # If reference_firewall_rule_id is set, the new rule # is inserted depending on the value of insert_before. # If insert_before is set, the new rule is inserted before # reference_firewall_rule_id, and if it is not set the new # rule is inserted after reference_firewall_rule_id. fwpra_db = self._get_policy_rule_association( context, id, ref_firewall_rule_id) if insert_before: position = fwpra_db.position else: position = fwpra_db.position + 1 else: # If reference_firewall_rule_id is not set, it is assumed # that the new rule needs to be inserted at the top. # insert_before field is ignored. # So default insertion is always at the top. # Also note that position numbering starts at 1. position = 1 return self._process_rule_for_policy(context, id, firewall_rule_id, position, None) def remove_rule(self, context, id, rule_info): firewall_rule_id = rule_info['firewall_rule_id'] with db_api.CONTEXT_WRITER.using(context): self._get_firewall_rule(context, firewall_rule_id) fwpra_db = self._get_policy_rule_association(context, id, firewall_rule_id) return self._process_rule_for_policy(context, id, firewall_rule_id, None, fwpra_db) def get_firewall_rule(self, context, id, fields=None): fwr = self._get_firewall_rule(context, id) policies = self.get_policies_with_rule(context, id) or None return self._make_firewall_rule_dict(fwr, fields, policies=policies) def get_firewall_rules(self, context, filters=None, fields=None): return model_query.get_collection( context, FirewallRuleV2, self._make_firewall_rule_dict, filters=filters, fields=fields) def _get_rules_in_policy(self, context, fwpid): """Gets rules in a firewall policy""" with db_api.CONTEXT_READER.using(context): fw_pol_rule_qry = context.session.query( FirewallPolicyRuleAssociation).filter_by( firewall_policy_id=fwpid) fwp_rules = [entry.firewall_rule_id for entry in fw_pol_rule_qry] return fwp_rules def get_policies_with_rule(self, context, fwrid): """Gets rules in a firewall policy""" with db_api.CONTEXT_READER.using(context): fw_pol_rule_qry = context.session.query( FirewallPolicyRuleAssociation).filter_by( firewall_rule_id=fwrid) fwps = [entry.firewall_policy_id for entry in fw_pol_rule_qry] return fwps def _set_rules_in_policy_rule_assoc(self, context, fwp_db, fwp): # Pull the rules and add it to policy - rule association table # Set the position (this can be used in the making the dict) # might be good to track the last position rule_id_list = fwp['firewall_rules'] if not rule_id_list: return position = 0 with db_api.CONTEXT_WRITER.using(context): for rule_id in rule_id_list: fw_pol_rul_db = FirewallPolicyRuleAssociation( firewall_policy_id=fwp_db['id'], firewall_rule_id=rule_id, position=position) context.session.add(fw_pol_rul_db) position += 1 def _check_rules_for_policy_is_valid(self, context, fwp, fwp_db, rule_id_list, filters): rules_in_fwr_db = model_query.get_collection_query( context, FirewallRuleV2, filters=filters) rules_dict = dict((fwr_db['id'], fwr_db) for fwr_db in rules_in_fwr_db) for fwrule_id in rule_id_list: if fwrule_id not in rules_dict: # Bail as soon as we find an invalid rule. raise f_exc.FirewallRuleNotFound( firewall_rule_id=fwrule_id) if 'shared' in fwp: if fwp['shared'] and not rules_dict[fwrule_id]['shared']: raise f_exc.FirewallRuleSharingConflict( firewall_rule_id=fwrule_id, firewall_policy_id=fwp_db['id']) elif fwp_db['shared'] and not rules_dict[fwrule_id]['shared']: raise f_exc.FirewallRuleSharingConflict( firewall_rule_id=fwrule_id, firewall_policy_id=fwp_db['id']) else: # the policy is not shared, the rule and policy should be in # the same project if the rule is not shared. if not rules_dict[fwrule_id]['shared']: if (rules_dict[fwrule_id]['tenant_id'] != fwp_db[ 'tenant_id']): raise f_exc.FirewallRuleConflict( firewall_rule_id=fwrule_id, project_id=rules_dict[fwrule_id]['tenant_id']) def _check_if_rules_shared_for_policy_shared(self, context, fwp_db, fwp): if fwp['shared']: rules_in_db = fwp_db.rule_associations for entry in rules_in_db: fwr_db = self._get_firewall_rule(context, entry.firewall_rule_id) if not fwr_db['shared']: raise f_exc.FirewallPolicySharingConflict( firewall_rule_id=fwr_db['id'], firewall_policy_id=fwp_db['id']) def get_fwgs_with_policy(self, context, fwp_id): with db_api.CONTEXT_READER.using(context): fwg_ing_pol_qry = context.session.query( FirewallGroup).filter_by( ingress_firewall_policy_id=fwp_id) ing_fwg_ids = [entry.id for entry in fwg_ing_pol_qry] fwg_eg_pol_qry = context.session.query( FirewallGroup).filter_by( egress_firewall_policy_id=fwp_id) eg_fwg_ids = [entry.id for entry in fwg_eg_pol_qry] return ing_fwg_ids, eg_fwg_ids def _check_fwgs_associated_with_policy_in_same_project(self, context, fwp_id, fwp_tenant_id): with db_api.CONTEXT_READER.using(context): fwg_with_fwp_id_db = context.session.query(FirewallGroup).filter( or_(FirewallGroup.ingress_firewall_policy_id == fwp_id, FirewallGroup.egress_firewall_policy_id == fwp_id)) for entry in fwg_with_fwp_id_db: if entry.tenant_id != fwp_tenant_id: raise f_exc.FirewallPolicyInUse( firewall_policy_id=fwp_id) def _delete_all_rules_from_policy(self, context, fwp_db): """Deletes all FirewallPolicyRuleAssociation objects fwp_db is an DB dict representing firewall policy. Returns a dictionary with updated rule_associations. """ for rule_id in [rule_assoc.firewall_rule_id for rule_assoc in fwp_db['rule_associations']]: fwpra_db = self._get_policy_rule_association( context, fwp_db['id'], rule_id) fwp_db.rule_associations.remove(fwpra_db) context.session.delete(fwpra_db) fwp_db.rule_associations = [] return fwp_db def _set_rules_for_policy(self, context, firewall_policy_db, fwp): rule_id_list = fwp['firewall_rules'] fwp_db = firewall_policy_db with db_api.CONTEXT_WRITER.using(context): if not rule_id_list: self._delete_all_rules_from_policy(context, fwp_db) return # We will first check if the new list of rules is valid filters = {'firewall_rule_id': [r_id for r_id in rule_id_list]} # Run a validation on the Firewall Rules table self._check_rules_for_policy_is_valid(context, fwp, fwp_db, rule_id_list, filters) # new rules are valid, lets delete the old association self._delete_all_rules_from_policy(context, fwp_db) # and add in the new association self._set_rules_in_policy_rule_assoc(context, fwp_db, fwp) # we need care about the associations related with this policy # and its rules only. filters['firewall_policy_id'] = [fwp_db['id']] rules_in_fpol_rul_db = model_query.get_collection_query( context, FirewallPolicyRuleAssociation, filters=filters) rules_dict = dict((fpol_rul_db['firewall_rule_id'], fpol_rul_db) for fpol_rul_db in rules_in_fpol_rul_db) fwp_db.rule_associations = [] for fwrule_id in rule_id_list: fwp_db.rule_associations.append(rules_dict[fwrule_id]) fwp_db.rule_associations.reorder() def _create_default_firewall_policy(self, context, tenant_id, policy_type, **kwargs): fwrs = kwargs.get('firewall_rules', []) description = kwargs.get('description', '') name = (const.DEFAULT_FWP_INGRESS if policy_type == 'ingress' else const.DEFAULT_FWP_EGRESS) firewall_policy = { 'name': name, 'description': description, 'audited': False, 'shared': False, 'firewall_rules': fwrs, 'tenant_id': tenant_id, } return self._do_create_firewall_policy(context, firewall_policy) def _do_create_firewall_policy(self, context, firewall_policy): fwp = firewall_policy with db_api.CONTEXT_WRITER.using(context): fwp_db = FirewallPolicy( id=uuidutils.generate_uuid(), tenant_id=fwp['tenant_id'], name=fwp['name'], description=fwp['description'], audited=fwp['audited'], shared=fwp['shared']) context.session.add(fwp_db) self._set_rules_for_policy(context, fwp_db, fwp) return self._make_firewall_policy_dict(fwp_db) def create_firewall_policy(self, context, firewall_policy): self._ensure_not_default_resource(firewall_policy, 'firewall_policy') return self._do_create_firewall_policy(context, firewall_policy) def update_firewall_policy(self, context, id, firewall_policy): fwp = firewall_policy with db_api.CONTEXT_WRITER.using(context): fwp_db = self._get_firewall_policy(context, id) self._ensure_not_default_resource(fwp_db, 'firewall_policy', action="update") if not fwp.get('shared', True): # an update is setting shared to False, make sure associated # firewall groups are in the same project. self._check_fwgs_associated_with_policy_in_same_project( context, id, fwp_db['tenant_id']) if 'shared' in fwp and 'firewall_rules' not in fwp: self._check_if_rules_shared_for_policy_shared( context, fwp_db, fwp) if 'firewall_rules' in fwp: self._set_rules_for_policy(context, fwp_db, fwp) del fwp['firewall_rules'] if 'audited' not in fwp: fwp['audited'] = False fwp_db.update(fwp) return self._make_firewall_policy_dict(fwp_db) def delete_firewall_policy(self, context, id): with db_api.CONTEXT_WRITER.using(context): fwp_db = self._get_firewall_policy(context, id) # check if policy in use qry = context.session.query(FirewallGroup) if qry.filter_by(ingress_firewall_policy_id=id).first(): raise f_exc.FirewallPolicyInUse(firewall_policy_id=id) elif qry.filter_by(egress_firewall_policy_id=id).first(): raise f_exc.FirewallPolicyInUse(firewall_policy_id=id) else: fwp_db = self._delete_all_rules_from_policy(context, fwp_db) context.session.delete(fwp_db) def get_firewall_policy(self, context, id, fields=None): fwp = self._get_firewall_policy(context, id) return self._make_firewall_policy_dict(fwp, fields) def get_firewall_policies(self, context, filters=None, fields=None): return model_query.get_collection( context, FirewallPolicy, self._make_firewall_policy_dict, filters=filters, fields=fields) def _set_ports_for_firewall_group(self, context, fwg_db, fwg): port_id_list = fwg['ports'] if not port_id_list: return exc_ports = [] for port_id in port_id_list: try: fwg_port_db = FirewallGroupPortAssociation( firewall_group_id=fwg_db['id'], port_id=port_id) context.session.add(fwg_port_db) except db_exc.DBDuplicateEntry: exc_ports.append(port_id) if exc_ports: raise f_exc.FirewallGroupPortInUse(port_ids=exc_ports) def get_ports_in_firewall_group(self, context, firewall_group_id): """Get the Ports associated with the firewall group.""" with db_api.CONTEXT_READER.using(context): fw_group_port_qry = context.session.query( FirewallGroupPortAssociation) fw_group_port_rows = fw_group_port_qry.filter_by( firewall_group_id=firewall_group_id) fw_ports = [entry.port_id for entry in fw_group_port_rows] return fw_ports def _delete_ports_in_firewall_group(self, context, firewall_group_id): """Delete the Ports associated with the firewall group.""" with db_api.CONTEXT_WRITER.using(context): fw_group_port_qry = context.session.query( FirewallGroupPortAssociation) fw_group_port_qry.filter_by( firewall_group_id=firewall_group_id).delete() return @db_api.CONTEXT_WRITER def _get_default_fwg_id(self, context, tenant_id): """Returns an id of default firewall group for given tenant or None""" default_fwg = model_query.query_with_hooks( context, FirewallGroup).filter_by( project_id=tenant_id, name=const.DEFAULT_FWG).first() if default_fwg: return default_fwg.id return None def get_fwg_attached_to_port(self, context, port_id): """Return a firewall group ID that is attached to a given port""" fwg_port = model_query.query_with_hooks( context, FirewallGroupPortAssociation).\ filter_by(port_id=port_id).first() if fwg_port: return fwg_port.firewall_group_id return None def get_fwg_ports_in_tenant(self, context, tenant_id): """Return a list of ports under a given tenant""" try: fwg_id = FirewallGroupPortAssociation.firewall_group_id with db_api.CONTEXT_READER.using(context): port_qry = context.session.query( FirewallGroupPortAssociation.port_id).join( FirewallGroup, FirewallGroup.id == fwg_id).filter( FirewallGroup.tenant_id == tenant_id).all() return list({port for (port,) in port_qry}) except exc.NoResultFound: return [] def _ensure_default_firewall_group(self, context, tenant_id): """Create a default firewall group if one doesn't exist for a tenant Returns the default firewall group id for a given tenant. """ exists = self._get_default_fwg_id(context, tenant_id) if exists: return exists try: # NOTE(cby): default fwg not created => we try to create it! with db_api.CONTEXT_WRITER.using(context): fwr_ids = self._create_default_firewall_rules( context, tenant_id) ingress_fwp = { 'description': 'Ingress firewall policy', 'firewall_rules': [fwr_ids['in_ipv4'], fwr_ids['in_ipv6']], } egress_fwp = { 'description': 'Egress firewall policy', 'firewall_rules': [fwr_ids['eg_ipv4'], fwr_ids['eg_ipv6']], } ingress_fwp_db = self._create_default_firewall_policy( context, tenant_id, 'ingress', **ingress_fwp) egress_fwp_db = self._create_default_firewall_policy( context, tenant_id, 'egress', **egress_fwp) fwg = { 'name': const.DEFAULT_FWG, 'tenant_id': tenant_id, 'ingress_firewall_policy_id': ingress_fwp_db['id'], 'egress_firewall_policy_id': egress_fwp_db['id'], 'ports': [], 'shared': False, 'status': nl_constants.INACTIVE, 'admin_state_up': True, 'description': 'Default firewall group', } fwg_db = self._create_firewall_group( context, fwg, default_fwg=True) context.session.add(DefaultFirewallGroup( firewall_group_id=fwg_db['id'], project_id=tenant_id)) return fwg_db['id'] except db_exc.DBDuplicateEntry: # NOTE(cby): default fwg created concurrently LOG.debug("Default FWG was concurrently created") return self._get_default_fwg_id(context, tenant_id) def _create_firewall_group(self, context, firewall_group, default_fwg=False): """Create a firewall group If default_fwg is True then a default firewall group is being created for a given tenant. """ fwg = firewall_group tenant_id = fwg['tenant_id'] if firewall_group.get('status') is None: fwg['status'] = nl_constants.CREATED if default_fwg: # A default firewall group is being created. default_fwg_id = self._get_default_fwg_id(context, tenant_id) if default_fwg_id is not None: # Default fwg for a given tenant exists, fetch it and return return self.get_firewall_group(context, default_fwg_id) else: # An ordinary firewall group is being created BUT let's make sure # that a default firewall group for given tenant exists self._ensure_default_firewall_group(context, tenant_id) with db_api.CONTEXT_WRITER.using(context): fwg_db = FirewallGroup( id=uuidutils.generate_uuid(), tenant_id=tenant_id, name=fwg['name'], description=fwg['description'], status=fwg['status'], ingress_firewall_policy_id=fwg['ingress_firewall_policy_id'], egress_firewall_policy_id=fwg['egress_firewall_policy_id'], admin_state_up=fwg['admin_state_up'], shared=fwg['shared']) context.session.add(fwg_db) self._set_ports_for_firewall_group(context, fwg_db, fwg) return self._make_firewall_group_dict(fwg_db) def create_firewall_group(self, context, firewall_group): self._ensure_not_default_resource(firewall_group, 'firewall_group') return self._create_firewall_group(context, firewall_group) def update_firewall_group(self, context, id, firewall_group): fwg = firewall_group # make sure that no group can be updated to have name=default self._ensure_not_default_resource(fwg, 'firewall_group') with db_api.CONTEXT_WRITER.using(context): fwg_db = self._get_firewall_group(context, id) if _is_default(fwg_db): attrs = [ 'name', 'description', 'admin_state_up', 'ingress_firewall_policy_id', 'egress_firewall_policy_id' ] if context.is_admin: attrs = ['name'] for attr in attrs: if attr in fwg: raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=fwg_db['id']) if 'ports' in fwg: LOG.debug("Ports are updated in Firewall Group") self._delete_ports_in_firewall_group(context, id) self._set_ports_for_firewall_group(context, fwg_db, fwg) del fwg['ports'] # If fwg is empty, skip updating if fwg: fwg_db.update( db_utils.filter_non_model_columns(fwg, FirewallGroup)) return self.get_firewall_group(context, id) def update_firewall_group_status(self, context, id, status, not_in=None): """Conditionally update firewall_group status. Status transition is performed only if firewall is not in the specified states as defined by 'not_in' list. """ # filter in_ wants iterable objects, None isn't. not_in = not_in or [] with db_api.CONTEXT_WRITER.using(context): return (context.session.query(FirewallGroup). filter(FirewallGroup.id == id). filter(~FirewallGroup.status.in_(not_in)). update({'status': status}, synchronize_session=False)) def delete_firewall_group(self, context, id): # Note: Plugin should ensure that it's okay to delete if the # firewall is active with db_api.CONTEXT_WRITER.using(context): # if no such group exists -> don't raise an exception according to # 80fe2ba1, return None try: fwg_db = self._get_firewall_group(context, id) except f_exc.FirewallGroupNotFound: return if _is_default(fwg_db): if context.is_admin: # Like Rules in Default SG, when the Default FWG is deleted # its associated Rules and policies would also be deleted. # Delete fwg first and then associated policies context.session.query( FirewallGroup).filter_by(id=id).delete() fwp = [fwg_db['ingress_firewall_policy_id'], fwg_db['egress_firewall_policy_id']] for fwp_id in fwp: self.delete_firewall_policy(context, fwp_id) else: # only admin can delete default fwg raise f_exc.FirewallGroupCannotRemoveDefault() else: context.session.query( FirewallGroup).filter_by(id=id).delete() def _ensure_not_default_resource(self, resource_dict, r_type, action=None): """Checks that a resource is not default by checking its name A resource_dict can be either a dictionary in form {r_type : {}} or a serialized object from db. Action is used to determine type of exception to be raised. """ resource = resource_dict.get(r_type) or resource_dict if r_type == 'firewall_group': if resource.get('name', '') == const.DEFAULT_FWG: if action == "update": raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=resource['id']) raise FirewallDefaultParameterExists( resource_type='Firewall Group', name=resource['name']) elif r_type == 'firewall_policy': if resource.get('name', '') in [const.DEFAULT_FWP_INGRESS, const.DEFAULT_FWP_EGRESS]: if action == "update": raise FirewallDefaultObjectUpdateRestricted( resource_type='Firewall Group', resource_id=resource['id']) raise FirewallDefaultParameterExists( resource_type='Firewall Policy', name=resource['name']) def get_firewall_group(self, context, id, fields=None): fw = self._get_firewall_group(context, id) return self._make_firewall_group_dict(fw, fields) def get_firewall_groups(self, context, filters=None, fields=None): if context.tenant_id: tenant_id = filters.get('tenant_id') if filters else None tenant_id = tenant_id[0] if tenant_id else context.tenant_id self._ensure_default_firewall_group(context, tenant_id) return model_query.get_collection( context, FirewallGroup, self._make_firewall_group_dict, filters=filters, fields=fields) def _is_default(fwg_db): return fwg_db['name'] == const.DEFAULT_FWG ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/0000775000175000017500000000000000000000000021636 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/__init__.py0000664000175000017500000000000000000000000023735 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2933502 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/0000775000175000017500000000000000000000000025466 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/README0000664000175000017500000000004600000000000026346 0ustar00zuulzuul00000000000000Generic single-database configuration.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/__init__.py0000664000175000017500000000000000000000000027565 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/env.py0000664000175000017500000000466100000000000026637 0ustar00zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from logging import config as logging_config from alembic import context from neutron_lib.db import model_base from oslo_config import cfg from oslo_db.sqlalchemy import session import sqlalchemy as sa from sqlalchemy import event MYSQL_ENGINE = None FWAAS_VERSION_TABLE = 'alembic_version_fwaas' 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'] = FWAAS_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=FWAAS_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() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/script.py.mako0000664000175000017500000000203500000000000030272 0ustar00zuulzuul00000000000000# Copyright ${create_date.year} # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """${message} Revision ID: ${up_revision} Revises: ${down_revision} Create Date: ${create_date} """ # revision identifiers, used by Alembic. revision = ${repr(up_revision)} down_revision = ${repr(down_revision)} % if branch_labels: branch_labels = ${repr(branch_labels)} %endif from alembic import op import sqlalchemy as sa ${imports if imports else ""} def upgrade(): ${upgrades if upgrades else "pass"} ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/0000775000175000017500000000000000000000000027336 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2653494 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/2023.2/0000775000175000017500000000000000000000000030064 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/2023.2/expand/0000775000175000017500000000000000000000000031343 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/2023.2/expand/6941ce70131e_add_standard_attr_id.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/2023.2/expand/6941ce70130000664000175000017500000000560100000000000032516 0ustar00zuulzuul00000000000000# Copyright 2023 EasyStack 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. # """add standard attributes Revision ID: 6941ce70131e Revises: f24e0d5e5bff Create Date: 2022-12-01 04:19:57.324584 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '6941ce70131e' down_revision = 'f24e0d5e5bff' tables = ['firewall_groups_v2', 'firewall_rules_v2', 'firewall_policies_v2'] standardattrs = sa.Table( 'standardattributes', sa.MetaData(), sa.Column('id', sa.BigInteger(), primary_key=True, autoincrement=True), sa.Column('resource_type', sa.String(length=255), nullable=False), sa.Column('description', sa.String(length=255), nullable=True)) def generate_records_for_existing(table): model = sa.Table(table, sa.MetaData(), sa.Column('id', sa.String(length=36), nullable=False), sa.Column('description', sa.String(length=255), nullable=True), sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) session = sa.orm.Session(bind=op.get_bind()) for row in session.query(model): res = session.execute( standardattrs.insert().values(resource_type=table, description=row[1]) ) session.execute( model.update().values( standard_attr_id=res.inserted_primary_key[0]).where( model.c.id == row[0]) ) session.commit() def upgrade(): for table in tables: op.add_column(table, sa.Column('standard_attr_id', sa.BigInteger(), nullable=True)) op.create_foreign_key( constraint_name=None, source_table=table, referent_table='standardattributes', local_cols=['standard_attr_id'], remote_cols=['id'], ondelete='CASCADE') generate_records_for_existing(table) op.alter_column(table, 'standard_attr_id', nullable=False, existing_type=sa.BigInteger(), existing_nullable=True, existing_server_default=False) op.create_unique_constraint( constraint_name='uniq_%s0standard_attr_id' % table, table_name=table, columns=['standard_attr_id']) op.drop_column(table, 'description') ././@PaxHeader0000000000000000000000000000021700000000000011455 xustar0000000000000000121 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/4202e3047e47_add_index_tenant_id.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/4202e3047e47_add_index_t0000664000175000017500000000210200000000000033170 0ustar00zuulzuul00000000000000# Copyright 2015 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. # """add_index_tenant_id Revision ID: 4202e3047e47 Revises: start_neutron_fwaas Create Date: 2015-02-10 17:17:47.846764 """ from alembic import op # revision identifiers, used by Alembic. revision = '4202e3047e47' down_revision = 'start_neutron_fwaas' TABLES = ['firewall_rules', 'firewalls', 'firewall_policies'] def upgrade(): for table in TABLES: op.create_index(op.f('ix_%s_tenant_id' % table), table, ['tenant_id'], unique=False) ././@PaxHeader0000000000000000000000000000022200000000000011451 xustar0000000000000000124 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/540142f314f4_fwaas_router_insertion.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/540142f314f4_fwaas_route0000664000175000017500000000403200000000000033246 0ustar00zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """FWaaS router insertion Revision ID: 540142f314f4 Revises: 4202e3047e47 Create Date: 2015-02-06 17:02:24.279337 """ from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection # revision identifiers, used by Alembic. revision = '540142f314f4' down_revision = '4202e3047e47' SQL_STATEMENT = ( "insert into firewall_router_associations " "select " "f.id as fw_id, r.id as router_id " "from firewalls f, routers r " "where " "f.tenant_id=r.%s" ) def upgrade(): op.create_table('firewall_router_associations', sa.Column('fw_id', sa.String(length=36), nullable=False), sa.Column('router_id', sa.String(length=36), nullable=False), sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['router_id'], ['routers.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('fw_id', 'router_id'), ) # Depending on when neutron-fwaas is installed with neutron, this script # may be run before or after the neutron core tables have had their # tenant_id columns renamed to project_id. Account for both scenarios. bind = op.get_bind() insp = reflection.Inspector.from_engine(bind) columns = insp.get_columns('routers') if 'tenant_id' in [c['name'] for c in columns]: op.execute(SQL_STATEMENT % 'tenant_id') else: op.execute(SQL_STATEMENT % 'project_id') ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/796c68dffbb_cisco_csr_fwaas.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/796c68dffbb_cisco_csr_fw0000664000175000017500000000301100000000000033631 0ustar00zuulzuul00000000000000# Copyright 2015 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. # """cisco_csr_fwaas Revision ID: 796c68dffbb Revises: 540142f314f4 Create Date: 2015-02-02 13:11:55.184112 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = '796c68dffbb' down_revision = '540142f314f4' def upgrade(active_plugins=None, options=None): op.create_table('cisco_firewall_associations', sa.Column('fw_id', sa.String(length=36), nullable=False), sa.Column('port_id', sa.String(length=36), nullable=True), sa.Column('direction', sa.String(length=16), nullable=True), sa.Column('acl_id', sa.String(length=36), nullable=True), sa.Column('router_id', sa.String(length=36), nullable=True), sa.ForeignKeyConstraint(['fw_id'], ['firewalls.id'], ondelete='CASCADE'), sa.ForeignKeyConstraint(['port_id'], ['ports.id'], ondelete='CASCADE'), sa.PrimaryKeyConstraint('fw_id') ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/CONTRACT_HEAD0000664000175000017500000000001500000000000031253 0ustar00zuulzuul00000000000000fd38cd995cc0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/EXPAND_HEAD0000664000175000017500000000001500000000000031015 0ustar00zuulzuul000000000000006941ce70131e ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/kilo_release.py0000664000175000017500000000151200000000000032345 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. # """kilo Revision ID: kilo Revises: 796c68dffbb Create Date: 2015-04-16 00:00:00.000000 """ # revision identifiers, used by Alembic. revision = 'kilo' down_revision = '796c68dffbb' def upgrade(): """A no-op migration for marking the Kilo release.""" pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2653494 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/0000775000175000017500000000000000000000000031010 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/0000775000175000017500000000000000000000000032625 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022300000000000011452 xustar0000000000000000125 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/67c8e8d61d5_initial.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/67c8e8d0000664000175000017500000000203100000000000033634 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 Liberty no-op script. Revision ID: 67c8e8d61d5 Revises: kilo Create Date: 2015-07-28 22:18:13.330846 """ from neutron.db import migration from neutron_lib.db import constants # revision identifiers, used by Alembic. revision = '67c8e8d61d5' down_revision = 'kilo' branch_labels = (constants.CONTRACT_BRANCH,) # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.LIBERTY] def upgrade(): pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/0000775000175000017500000000000000000000000032267 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023200000000000011452 xustar0000000000000000132 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/4b47ea298795_add_reject_rule.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/4b47ea2980000664000175000017500000000277200000000000033453 0ustar00zuulzuul00000000000000# Copyright 2015 NEC Corporation. All rights reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. """add reject rule Revision ID: 4b47ea298795 Revises: c40fbb377ad Create Date: 2015-04-15 04:19:57.324584 """ import sqlalchemy as sa from neutron.db import migration # revision identifiers, used by Alembic. revision = '4b47ea298795' down_revision = 'c40fbb377ad' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.LIBERTY, migration.MITAKA] new_action = sa.Enum('allow', 'deny', 'reject', name='firewallrules_action') def upgrade(): # NOTE: postgresql have a builtin ENUM type, so just altering the # column won't works # https://bitbucket.org/zzzeek/alembic/issues/270/altering-enum-type # alter_enum that was already invented for such case in neutron # https://github.com/openstack/neutron/blob/master/neutron/db/migration/__init__.py migration.alter_enum( 'firewall_rules', 'action', enum_type=new_action, nullable=True) ././@PaxHeader0000000000000000000000000000022100000000000011450 xustar0000000000000000123 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/c40fbb377ad_initial.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/c40fbb3770000664000175000017500000000163300000000000033516 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 Liberty no-op script. Revision ID: c40fbb377ad Revises: kilo Create Date: 2015-07-28 22:18:13.321233 """ from neutron_lib.db import constants # revision identifiers, used by Alembic. revision = 'c40fbb377ad' down_revision = 'kilo' branch_labels = (constants.EXPAND_BRANCH,) def upgrade(): pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2653494 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/0000775000175000017500000000000000000000000030604 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/0000775000175000017500000000000000000000000032421 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023100000000000011451 xustar0000000000000000131 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/458aa42b14b_fw_table_alter.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/458aa42b0000664000175000017500000000256300000000000033504 0ustar00zuulzuul00000000000000# Copyright 2015 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. # """fw_table_alter script to make column case sensitive Revision ID: 458aa42b14b Revises: 67c8e8d61d5 Create Date: 2015-09-16 11:47:43.061649 """ from alembic import op from neutron.db import migration # revision identifiers, used by Alembic. revision = '458aa42b14b' down_revision = '67c8e8d61d5' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.MITAKA] FW_TAB_NAME = ['firewall_rules', 'firewall_policies', 'firewalls'] SQL_STATEMENT_UPDATE_CMD = ( "alter table %s " "modify name varchar(255) " "CHARACTER SET utf8 COLLATE utf8_bin" ) def upgrade(): context = op.get_context() if context.bind.dialect.name == 'mysql': for table in FW_TAB_NAME: op.execute(SQL_STATEMENT_UPDATE_CMD % table) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2693496 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/0000775000175000017500000000000000000000000030650 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/0000775000175000017500000000000000000000000032465 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000024400000000000011455 xustar0000000000000000142 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/f83a0b2964d0_rename_tenant_to_project.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/f83a0b290000664000175000017500000000654500000000000033560 0ustar00zuulzuul00000000000000# Copyright 2016 # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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: f83a0b2964d0 Revises: 458aa42b14b Create Date: 2016-07-14 13:11:53.112622 """ from alembic import op import sqlalchemy as sa from sqlalchemy.engine import reflection from neutron.db import migration # revision identifiers, used by Alembic. revision = 'f83a0b2964d0' down_revision = '458aa42b14b' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] _INSPECTOR = None def get_inspector(): """Reuse inspector""" global _INSPECTOR if _INSPECTOR: return _INSPECTOR else: bind = op.get_bind() _INSPECTOR = reflection.Inspector.from_engine(bind) return _INSPECTOR def get_tables(): """Returns hardcoded list of tables which have ``tenant_id`` column. The list is hardcoded to match the state of the schema when this upgrade script is run. """ tables = [ 'firewalls', 'firewall_policies', 'firewall_rules', ] 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(index_name=op.f(old_name), table_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() } ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/0000775000175000017500000000000000000000000032127 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023400000000000011454 xustar0000000000000000134 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/d6a12e637e28_neutron_fwaas_v2_0.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/d6a12e637e0000664000175000017500000001064500000000000033447 0ustar00zuulzuul00000000000000# Copyright 2016 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. # """neutron-fwaas v2.0 Revision ID: d6a12e637e28 Revises: 4b47ea298795 Create Date: 2016-06-08 19:57:13.848855 """ from alembic import op import sqlalchemy as sa from sqlalchemy.dialects import postgresql from neutron.db import migration # revision identifiers, used by Alembic. revision = 'd6a12e637e28' down_revision = '4b47ea298795' # milestone identifier, used by neutron-db-manage neutron_milestone = [migration.NEWTON] def get_enum(): engine = op.get_bind().engine # In PostgreSQL types created separately, so if type was already created in # 4b47ea298795_add_reject_rule it should be created one time. # Use parameter create_type=False for that. if engine.name == 'postgresql': return postgresql.ENUM('allow', 'deny', 'reject', name='firewallrules_action', create_type=False) else: return sa.Enum('allow', 'deny', 'reject', name='firewallrules_action') def upgrade(): op.create_table( 'firewall_policies_v2', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('name', sa.String(length=255)), sa.Column('description', sa.String(length=1024)), sa.Column('project_id', sa.String(length=255), index=True), sa.Column('audited', sa.Boolean), sa.Column('public', sa.Boolean), sa.Column('rule_count', sa.Integer)) op.create_table( 'firewall_rules_v2', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('name', sa.String(length=255)), sa.Column('description', sa.String(length=1024)), sa.Column('project_id', sa.String(length=255), index=True), sa.Column('protocol', sa.String(length=40)), sa.Column('ip_version', sa.Integer), sa.Column('source_ip_address', sa.String(length=46)), sa.Column('destination_ip_address', sa.String(length=46)), sa.Column('source_port_range_min', sa.Integer), sa.Column('source_port_range_max', sa.Integer), sa.Column('destination_port_range_min', sa.Integer), sa.Column('destination_port_range_max', sa.Integer), sa.Column('action', get_enum()), sa.Column('public', sa.Boolean), sa.Column('enabled', sa.Boolean)) op.create_table( 'firewall_groups_v2', sa.Column('id', sa.String(length=36), primary_key=True), sa.Column('name', sa.String(length=255)), sa.Column('description', sa.String(length=1024)), sa.Column('project_id', sa.String(length=255), index=True), sa.Column('status', sa.String(length=16)), sa.Column('admin_state_up', sa.Boolean), sa.Column('public', sa.Boolean), sa.Column('egress_firewall_policy_id', sa.String(length=36), sa.ForeignKey('firewall_policies_v2.id')), sa.Column('ingress_firewall_policy_id', sa.String(length=36), sa.ForeignKey('firewall_policies_v2.id'))) op.create_table( 'firewall_group_port_associations_v2', sa.Column('firewall_group_id', sa.String(length=36), sa.ForeignKey('firewall_groups_v2.id', ondelete='CASCADE'), nullable=False), sa.Column('port_id', sa.String(length=36), sa.ForeignKey('ports.id', ondelete='CASCADE'), nullable=False) ) op.create_table( 'firewall_policy_rule_associations_v2', sa.Column('firewall_policy_id', sa.String(length=36), sa.ForeignKey('firewall_policies_v2.id', ondelete='CASCADE'), nullable=False, primary_key=True), sa.Column('firewall_rule_id', sa.String(length=36), sa.ForeignKey('firewall_rules_v2.id', ondelete='CASCADE'), nullable=False, primary_key=True), sa.Column('position', sa.Integer)) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2693496 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/pike/0000775000175000017500000000000000000000000030266 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/0000775000175000017500000000000000000000000032103 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000026100000000000011454 xustar0000000000000000155 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/fd38cd995cc0_shared_attribute_for_firewall_resources.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/fd38cd995c0000664000175000017500000000240600000000000033515 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 shared attribute for firewall resource Revision ID: fd38cd995cc0 Revises: f83a0b2964d0 Create Date: 2017-03-31 14:22:21.063392 """ from alembic import op import sqlalchemy as sa # revision identifiers, used by Alembic. revision = 'fd38cd995cc0' down_revision = 'f83a0b2964d0' depends_on = ('d6a12e637e28',) def upgrade(): op.alter_column('firewall_rules_v2', 'public', new_column_name='shared', existing_type=sa.Boolean) op.alter_column('firewall_groups_v2', 'public', new_column_name='shared', existing_type=sa.Boolean) op.alter_column('firewall_policies_v2', 'public', new_column_name='shared', existing_type=sa.Boolean) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2693496 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/queens/0000775000175000017500000000000000000000000030636 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/0000775000175000017500000000000000000000000032115 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000025600000000000011460 xustar0000000000000000152 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/876782258a43_create_default_firewall_groups_table.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/876782258a0000664000175000017500000000456300000000000033235 0ustar00zuulzuul00000000000000# Copyright 2017 FUJITSU 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. # """create_default_firewall_groups_table Revision ID: 876782258a43 Revises: d6a12e637e28 Create Date: 2017-01-26 23:47:42.795504 """ from alembic import op from neutron_lib.db import constants as db_constants from neutron_lib import exceptions import sqlalchemy as sa from neutron_fwaas._i18n import _ from neutron_fwaas.common import fwaas_constants as const from neutron_fwaas.common import resources # revision identifiers, used by Alembic. revision = '876782258a43' down_revision = 'd6a12e637e28' class DuplicateDefaultFirewallGroup(exceptions.Conflict): message = _("Duplicate Firewall group found named '%s'. " "Database cannot be upgraded. Please, remove all duplicates " "before upgrading the database.") % const.DEFAULT_FWG def upgrade(): op.create_table( 'default_firewall_groups', sa.Column('project_id', sa.String(length=db_constants.PROJECT_ID_FIELD_SIZE), nullable=False), sa.Column('firewall_group_id', sa.String(length=db_constants.UUID_FIELD_SIZE), nullable=False), sa.PrimaryKeyConstraint('project_id'), sa.ForeignKeyConstraint(['firewall_group_id'], ['firewall_groups_v2.id'], ondelete="CASCADE")) def check_sanity(connection): # check for already existing firewall groups with name == DEFAULT_FWG insp = sa.engine.reflection.Inspector.from_engine(connection) if 'firewall_groups_v2' not in insp.get_table_names(): return [] session = sa.orm.Session(bind=connection) default_fwg = session.query(resources.FIREWALL_GROUP.name).filter( resources.FIREWALL_GROUP.name == const.DEFAULT_FWG).first() if default_fwg: raise DuplicateDefaultFirewallGroup() ././@PaxHeader0000000000000000000000000000026000000000000011453 xustar0000000000000000154 path=neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/f24e0d5e5bff_uniq_firewallgroupportassociation0port.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/f24e0d5e5b0000664000175000017500000000454100000000000033511 0ustar00zuulzuul00000000000000# Copyright 2017 Fujitsu 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. # """uniq_firewallgroupportassociation0port Revision ID: f24e0d5e5bff Revises: 876782258a43 Create Date: 2017-11-08 15:55:40.990272 """ from alembic import op from neutron_lib import exceptions import sqlalchemy as sa from neutron._i18n import _ # revision identifiers, used by Alembic. revision = 'f24e0d5e5bff' down_revision = '876782258a43' fwg_port_association = sa.Table( 'firewall_group_port_associations_v2', sa.MetaData(), sa.Column('firewall_group_id', sa.String(36)), sa.Column('port_id', sa.String(36))) class DuplicatePortRecordinFirewallGroupPortAssociation(exceptions.Conflict): message = _("Duplicate port(s) %(port_id)s records exist in" "firewall_group_port_associations_v2 table. Database cannot" "be upgraded. Please remove all duplicated records before" "upgrading the database.") def upgrade(): op.create_unique_constraint( 'uniq_firewallgroupportassociation0port_id', 'firewall_group_port_associations_v2', ['port_id']) def check_sanity(connection): duplicated_port_ids = ( get_duplicate_port_records_in_fwg_port_association(connection)) if duplicated_port_ids: raise DuplicatePortRecordinFirewallGroupPortAssociation( port_id=",".join(duplicated_port_ids)) def get_duplicate_port_records_in_fwg_port_association(connection): insp = sa.engine.reflection.Inspector.from_engine(connection) if 'firewall_group_port_associations_v2' not in insp.get_table_names(): return [] session = sa.orm.Session(bind=connection) query = (session.query(fwg_port_association.c.port_id) .group_by(fwg_port_association.c.port_id) .having(sa.func.count() > 1)).all() return [q[0] for q in query] ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/migration/alembic_migrations/versions/start_neutron_fwaas.py0000664000175000017500000000153300000000000034002 0ustar00zuulzuul00000000000000# Copyright 2014 OpenStack Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # """start neutron-fwaas chain Revision ID: start_neutron_fwaas Revises: None Create Date: 2014-12-09 18:42:08.262632 """ # revision identifiers, used by Alembic. revision = 'start_neutron_fwaas' down_revision = None def upgrade(): pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/db/models/0000775000175000017500000000000000000000000021130 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/models/__init__.py0000664000175000017500000000000000000000000023227 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/db/models/head.py0000664000175000017500000000120200000000000022376 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.db import model_base def get_metadata(): return model_base.BASEV2.metadata ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2973504 neutron-fwaas-20.0.0/neutron_fwaas/extensions/0000775000175000017500000000000000000000000021457 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/extensions/__init__.py0000664000175000017500000000000000000000000023556 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/extensions/firewall_v2.py0000664000175000017500000001762200000000000024255 0ustar00zuulzuul00000000000000# Copyright (c) 2016 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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.v2 import resource_helper from neutron_lib.api.definitions import constants as api_const from neutron_lib.api.definitions import firewall_v2 from neutron_lib.api import extensions from neutron_lib.services import base as service_base from oslo_config import cfg from neutron_fwaas._i18n import _ from neutron_fwaas.common import fwaas_constants default_fwg_rules_opts = [ cfg.StrOpt('ingress_action', default=api_const.FWAAS_DENY, help=_('Firewall group rule action allow or ' 'deny or reject for ingress. ' 'Default is deny.')), cfg.StrOpt('ingress_source_ipv4_address', default=None, help=_('IPv4 source address for ingress ' '(address or address/netmask). ' 'Default is None.')), cfg.StrOpt('ingress_source_ipv6_address', default=None, help=_('IPv6 source address for ingress ' '(address or address/netmask). ' 'Default is None.')), cfg.StrOpt('ingress_source_port', default=None, help=_('Source port number or range ' '(min:max) for ingress. ' 'Default is None.')), cfg.StrOpt('ingress_destination_ipv4_address', default=None, help=_('IPv4 destination address for ingress ' '(address or address/netmask). ' 'Default is None.')), cfg.StrOpt('ingress_destination_ipv6_address', default=None, help=_('IPv6 destination address for ingress ' '(address or address/netmask). ' 'Default is deny.')), cfg.StrOpt('ingress_destination_port', default=None, help=_('Destination port number or range ' '(min:max) for ingress. ' 'Default is None.')), cfg.StrOpt('egress_action', default=api_const.FWAAS_ALLOW, help=_('Firewall group rule action allow or ' 'deny or reject for egress. ' 'Default is allow.')), cfg.StrOpt('egress_source_ipv4_address', default=None, help=_('IPv4 source address for egress ' '(address or address/netmask). ' 'Default is None.')), cfg.StrOpt('egress_source_ipv6_address', default=None, help=_('IPv6 source address for egress ' '(address or address/netmask). ' 'Default is deny.')), cfg.StrOpt('egress_source_port', default=None, help=_('Source port number or range ' '(min:max) for egress. ' 'Default is None.')), cfg.StrOpt('egress_destination_ipv4_address', default=None, help=_('IPv4 destination address for egress ' '(address or address/netmask). ' 'Default is deny.')), cfg.StrOpt('egress_destination_ipv6_address', default=None, help=_('IPv6 destination address for egress ' '(address or address/netmask). ' 'Default is deny.')), cfg.StrOpt('egress_destination_port', default=None, help=_('Destination port number or range ' '(min:max) for egress. ' 'Default is None.')), cfg.BoolOpt('shared', default=False, help=_('Firewall group rule shared. ' 'Default is False.')), cfg.StrOpt('protocol', default=None, help=_('Network protocols (tcp, udp, ...). ' 'Default is None.')), cfg.BoolOpt('enabled', default=True, help=_('Firewall group rule enabled. ' 'Default is True.')), ] firewall_quota_opts = [ cfg.IntOpt('quota_firewall_group', default=10, help=_('Number of firewall groups allowed per tenant. ' 'A negative value means unlimited.')), cfg.IntOpt('quota_firewall_policy', default=10, help=_('Number of firewall policies allowed per tenant. ' 'A negative value means unlimited.')), cfg.IntOpt('quota_firewall_rule', default=100, help=_('Number of firewall rules allowed per tenant. ' 'A negative value means unlimited.')), ] cfg.CONF.register_opts(default_fwg_rules_opts, 'default_fwg_rules') cfg.CONF.register_opts(firewall_quota_opts, 'QUOTAS') class Firewall_v2(extensions.APIExtensionDescriptor): api_definition = firewall_v2 @classmethod def get_resources(cls): special_mappings = {'firewall_policies': 'firewall_policy'} plural_mappings = resource_helper.build_plural_mappings( special_mappings, firewall_v2.RESOURCE_ATTRIBUTE_MAP) return resource_helper.build_resource_info( plural_mappings, firewall_v2.RESOURCE_ATTRIBUTE_MAP, fwaas_constants.FIREWALL_V2, action_map=firewall_v2.ACTION_MAP, register_quota=True) @classmethod def get_plugin_interface(cls): return Firewallv2PluginBase class Firewallv2PluginBase(service_base.ServicePluginBase, metaclass=abc.ABCMeta): def get_plugin_type(self): return fwaas_constants.FIREWALL_V2 def get_plugin_description(self): return 'Firewall Service v2 Plugin' # Firewall Group @abc.abstractmethod def create_firewall_group(self, context, firewall_group): pass @abc.abstractmethod def delete_firewall_group(self, context, id): pass @abc.abstractmethod def get_firewall_group(self, context, id, fields=None): pass @abc.abstractmethod def get_firewall_groups(self, context, filters=None, fields=None): pass @abc.abstractmethod def update_firewall_group(self, context, id, firewall_group): pass # Firewall Policy @abc.abstractmethod def create_firewall_policy(self, context, firewall_policy): pass @abc.abstractmethod def delete_firewall_policy(self, context, id): pass @abc.abstractmethod def get_firewall_policy(self, context, id, fields=None): pass @abc.abstractmethod def get_firewall_policies(self, context, filters=None, fields=None): pass @abc.abstractmethod def update_firewall_policy(self, context, id, firewall_policy): pass # Firewall Rule @abc.abstractmethod def create_firewall_rule(self, context, firewall_rule): pass @abc.abstractmethod def delete_firewall_rule(self, context, id): pass @abc.abstractmethod def get_firewall_rule(self, context, id, fields=None): pass @abc.abstractmethod def get_firewall_rules(self, context, filters=None, fields=None): pass @abc.abstractmethod def update_firewall_rule(self, context, id, firewall_rule): pass @abc.abstractmethod def insert_rule(self, context, id, rule_info): pass @abc.abstractmethod def remove_rule(self, context, id, rule_info): pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/extensions/firewall_v2_stdattrs.py0000664000175000017500000000145400000000000026201 0ustar00zuulzuul00000000000000# Copyright 2023 EasyStack 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.api.definitions import firewall_v2_stdattrs as apidef from neutron_lib.api import extensions class Standard_attr_fwaas(extensions.APIExtensionDescriptor): api_definition = apidef ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/opts.py0000664000175000017500000000233200000000000020617 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 neutron.conf.services.provider_configuration import neutron_fwaas.services.firewall.service_drivers.agents.\ firewall_agent_api import neutron_fwaas.extensions.firewall_v2 def list_agent_opts(): return [ ('fwaas', neutron_fwaas.services.firewall.service_drivers.agents. firewall_agent_api.FWaaSOpts), ] def list_opts(): return [ ('quotas', neutron_fwaas.extensions.firewall_v2.firewall_quota_opts), ('service_providers', neutron.conf.services.provider_configuration.serviceprovider_opts), ('default_fwg_rules', neutron_fwaas.extensions.firewall_v2.default_fwg_rules_opts), ] ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/policies/0000775000175000017500000000000000000000000021067 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/policies/__init__.py0000664000175000017500000000161200000000000023200 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_fwaas.policies import firewall_group from neutron_fwaas.policies import firewall_policy from neutron_fwaas.policies import firewall_rule def list_rules(): return itertools.chain( firewall_group.list_rules(), firewall_policy.list_rules(), firewall_rule.list_rules(), ) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/policies/firewall_group.py0000664000175000017500000000571400000000000024471 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 import policy as base from oslo_policy import policy rules = [ policy.RuleDefault( 'shared_firewall_groups', 'field:firewall_groups:shared=True', 'Definition of shared firewall groups' ), policy.DocumentedRuleDefault( 'create_firewall_group', base.RULE_ANY, 'Create a firewall group', [ { 'method': 'POST', 'path': '/fwaas/firewall_groups', }, ] ), policy.DocumentedRuleDefault( 'update_firewall_group', base.RULE_ADMIN_OR_OWNER, 'Update a firewall group', [ { 'method': 'PUT', 'path': '/fwaas/firewall_groups/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_firewall_group', base.RULE_ADMIN_OR_OWNER, 'Delete a firewall group', [ { 'method': 'DELETE', 'path': '/fwaas/firewall_groups/{id}', }, ] ), policy.DocumentedRuleDefault( 'create_firewall_group:shared', base.RULE_ADMIN_ONLY, 'Create a shared firewall group', [ { 'method': 'POST', 'path': '/fwaas/firewall_groups', }, ] ), policy.DocumentedRuleDefault( 'update_firewall_group:shared', base.RULE_ADMIN_ONLY, 'Update ``shared`` attribute of a firewall group', [ { 'method': 'PUT', 'path': '/fwaas/firewall_groups/{id}', }, ] ), # TODO(amotoki): Drop this rule as it has no effect. policy.DocumentedRuleDefault( 'delete_firewall_group:shared', base.RULE_ADMIN_ONLY, 'Delete a shared firewall group', [ { 'method': 'DELETE', 'path': '/fwaas/firewall_groups/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_firewall_group', 'rule:admin_or_owner or rule:shared_firewall_groups', 'Get firewall groups', [ { 'method': 'GET', 'path': '/fwaas/firewall_groups', }, { 'method': 'GET', 'path': '/fwaas/firewall_groups/{id}', }, ] ), ] def list_rules(): return rules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/policies/firewall_policy.py0000664000175000017500000000576300000000000024640 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 import policy as base from oslo_policy import policy rules = [ policy.RuleDefault( 'shared_firewall_policies', 'field:firewall_policies:shared=True', 'Definition of shared firewall policies' ), policy.DocumentedRuleDefault( 'create_firewall_policy', base.RULE_ANY, 'Create a firewall policy', [ { 'method': 'POST', 'path': '/fwaas/firewall_policies', }, ] ), policy.DocumentedRuleDefault( 'update_firewall_policy', base.RULE_ADMIN_OR_OWNER, 'Update a firewall policy', [ { 'method': 'PUT', 'path': '/fwaas/firewall_policies/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_firewall_policy', base.RULE_ADMIN_OR_OWNER, 'Delete a firewall policy', [ { 'method': 'DELETE', 'path': '/fwaas/firewall_policies/{id}', }, ] ), policy.DocumentedRuleDefault( 'create_firewall_policy:shared', base.RULE_ADMIN_ONLY, 'Create a shared firewall policy', [ { 'method': 'POST', 'path': '/fwaas/firewall_policies', }, ] ), policy.DocumentedRuleDefault( 'update_firewall_policy:shared', base.RULE_ADMIN_ONLY, 'Update ``shared`` attribute of a firewall policy', [ { 'method': 'PUT', 'path': '/fwaas/firewall_policies/{id}', }, ] ), # TODO(amotoki): Drop this rule as it has no effect. policy.DocumentedRuleDefault( 'delete_firewall_policy:shared', base.RULE_ADMIN_ONLY, 'Delete a shread firewall policy', [ { 'method': 'DELETE', 'path': '/fwaas/firewall_policies/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_firewall_policy', 'rule:admin_or_owner or rule:shared_firewall_policies', 'Get firewall policies', [ { 'method': 'GET', 'path': '/fwaas/firewall_policies', }, { 'method': 'GET', 'path': '/fwaas/firewall_policies/{id}', }, ] ), ] def list_rules(): return rules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/policies/firewall_rule.py0000664000175000017500000000700100000000000024273 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 import policy as base from oslo_policy import policy rules = [ policy.RuleDefault( 'shared_firewall_rules', 'field:firewall_rules:shared=True', 'Definition of shared firewall rules' ), policy.DocumentedRuleDefault( 'create_firewall_rule', base.RULE_ANY, 'Create a firewall rule', [ { 'method': 'POST', 'path': '/fwaas/firewall_rules', }, ] ), policy.DocumentedRuleDefault( 'update_firewall_rule', base.RULE_ADMIN_OR_OWNER, 'Update a firewall rule', [ { 'method': 'PUT', 'path': '/fwaas/firewall_rules/{id}', }, ] ), policy.DocumentedRuleDefault( 'delete_firewall_rule', base.RULE_ADMIN_OR_OWNER, 'Delete a firewall rule', [ { 'method': 'DELETE', 'path': '/fwaas/firewall_rules/{id}', }, ] ), policy.DocumentedRuleDefault( 'create_firewall_rule:shared', base.RULE_ADMIN_ONLY, 'Create a shared firewall rule', [ { 'method': 'POST', 'path': '/fwaas/firewall_rules', }, ] ), policy.DocumentedRuleDefault( 'update_firewall_rule:shared', base.RULE_ADMIN_ONLY, 'Update ``shared`` attribute of a firewall rule', [ { 'method': 'PUT', 'path': '/fwaas/firewall_rules/{id}', }, ] ), # TODO(amotoki): Drop this rule as it has no effect. policy.DocumentedRuleDefault( 'delete_firewall_rule:shared', base.RULE_ADMIN_ONLY, 'Delete a shread firewall rule', [ { 'method': 'DELETE', 'path': '/fwaas/firewall_rules/{id}', }, ] ), policy.DocumentedRuleDefault( 'get_firewall_rule', 'rule:admin_or_owner or rule:shared_firewall_rules', 'Get firewall rules', [ { 'method': 'GET', 'path': '/fwaas/firewall_rules', }, { 'method': 'GET', 'path': '/fwaas/firewall_rules/{id}', }, ] ), policy.DocumentedRuleDefault( 'insert_rule', base.RULE_ADMIN_OR_OWNER, 'Insert rule into a firewall policy', [ { 'method': 'PUT', 'path': '/fwaas/firewall_policies/{id}/insert_rule', }, ] ), policy.DocumentedRuleDefault( 'remove_rule', base.RULE_ADMIN_OR_OWNER, 'Remove rule from a firewall policy', [ { 'method': 'PUT', 'path': '/fwaas/firewall_policies/{id}/remove_rule', }, ] ), ] def list_rules(): return rules ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/privileged/0000775000175000017500000000000000000000000021412 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/__init__.py0000664000175000017500000000225100000000000023523 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_privsep import capabilities as c from oslo_privsep import priv_context # It is expected that most (if not all) neutron-fwaas operations can be # executed with these privileges. default = priv_context.PrivContext( __name__, cfg_section='privsep', pypath=__name__ + '.default', # TODO(gus): CAP_SYS_ADMIN is required (only?) for manipulating # network namespaces. SYS_ADMIN is a lot of scary powers, so # consider breaking this out into a separate minimal context. capabilities=[c.CAP_SYS_ADMIN, c.CAP_NET_ADMIN], ) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/privileged/netfilter_log/0000775000175000017500000000000000000000000024247 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/netfilter_log/__init__.py0000664000175000017500000000000000000000000026346 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/netfilter_log/libnetfilter_log.py0000664000175000017500000002453100000000000030152 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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 multiprocessing import socket import struct import time import cffi import eventlet from eventlet.green import zmq from neutron_lib.utils import runtime from os_ken.lib import addrconv from os_ken.lib.packet import arp from os_ken.lib.packet import ether_types from os_ken.lib.packet import ethernet from os_ken.lib.packet import ipv4 from os_ken.lib.packet import ipv6 from oslo_log import log as logging from oslo_serialization import jsonutils from oslo_utils import encodeutils from oslo_utils import excutils from neutron_fwaas._i18n import _ from neutron_fwaas import privileged from neutron_fwaas.privileged import utils as fwaas_utils LOG = logging.getLogger(__name__) # TODO(annp): consider to make a pub-sub pattern which allows other logging # driver like snat log can consume libnetfilter_log NETFILTER_LOG = 'netfilter_log' ADDR_IPC = "ipc:///var/run/nflog" CDEF = ''' typedef unsigned char u_int8_t; typedef unsigned short int u_int16_t; typedef unsigned int u_int32_t; struct nfulnl_msg_packet_hdr { u_int16_t hw_protocol; // hw protocol (network order) u_int8_t hook; // netfilter hook u_int8_t _pad; }; int nflog_fd(struct nflog_handle *h); ssize_t recv(int sockfd, void *buf, size_t len, int flags); struct nflog_handle *nflog_open(void); int nflog_close(struct nflog_handle *h); int nflog_bind_pf(struct nflog_handle *h, u_int16_t pf); int nflog_unbind_pf(struct nflog_handle *h, u_int16_t pf); struct nflog_g_handle *nflog_bind_group(struct nflog_handle *h, u_int16_t num); int nflog_unbind_group(struct nflog_g_handle *gh); static const u_int8_t NFULNL_COPY_PACKET; int nflog_set_mode(struct nflog_g_handle *gh, u_int8_t mode, unsigned int len); int nflog_set_timeout(struct nflog_g_handle *gh, u_int32_t timeout); int nflog_set_flags(struct nflog_g_handle *gh, u_int16_t flags); int nflog_set_qthresh(struct nflog_g_handle *gh, u_int32_t qthresh); int nflog_set_nlbufsiz(struct nflog_g_handle *gh, u_int32_t nlbufsiz); typedef int nflog_callback(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg, struct nflog_data *nfd, void *data); int nflog_callback_register( struct nflog_g_handle *gh, nflog_callback *cb, void *data); int nflog_handle_packet(struct nflog_handle *h, char *buf, int len); struct nfulnl_msg_packet_hdr *nflog_get_msg_packet_hdr( struct nflog_data *nfad); u_int16_t nflog_get_hwtype(struct nflog_data *nfad); u_int16_t nflog_get_msg_packet_hwhdrlen(struct nflog_data *nfad); char *nflog_get_msg_packet_hwhdr(struct nflog_data *nfad); u_int32_t nflog_get_nfmark(struct nflog_data *nfad); int nflog_get_timestamp(struct nflog_data *nfad, struct timeval *tv); u_int32_t nflog_get_indev(struct nflog_data *nfad); u_int32_t nflog_get_physindev(struct nflog_data *nfad); u_int32_t nflog_get_outdev(struct nflog_data *nfad); u_int32_t nflog_get_physoutdev(struct nflog_data *nfad); struct nfulnl_msg_packet_hw *nflog_get_packet_hw(struct nflog_data *nfad); int nflog_get_payload(struct nflog_data *nfad, char **data); char *nflog_get_prefix(struct nflog_data *nfad); ''' ffi = None libnflog = None def init_library(): """Load libnetfilter_log library""" global ffi global libnflog if not ffi: ffi = cffi.FFI() ffi.cdef(CDEF) if not libnflog: try: libnflog = ffi.dlopen(NETFILTER_LOG) except OSError: msg = "Could not found libnetfilter-log" raise Exception(msg) return ffi, libnflog ffi, libnflog = init_library() def _payload(nfa): buf = ffi.new('char **') pkt_len = libnflog.nflog_get_payload(nfa, buf) if pkt_len <= 0: return None return ffi.buffer(buf[0], pkt_len)[:] def decode(nfa): """This function analyses nflog packet by using os-ken packet library.""" prefix = ffi.string(libnflog.nflog_get_prefix(nfa)) packet_hdr = libnflog.nflog_get_msg_packet_hdr(nfa) hw_proto = socket.ntohs(packet_hdr.hw_protocol) msg = '' msg_packet_hwhdr = libnflog.nflog_get_msg_packet_hwhdr(nfa) if msg_packet_hwhdr != ffi.NULL: packet_hwhdr = ffi.string(msg_packet_hwhdr) if len(packet_hwhdr) >= 12: dst, src = struct.unpack_from('!6s6s', packet_hwhdr) # Dump ethernet packet to get mac addresses eth = ethernet.ethernet(addrconv.mac.bin_to_text(dst), addrconv.mac.bin_to_text(src), ethertype=hw_proto) msg = str(eth) # Dump IP packet pkt = _payload(nfa) if hw_proto == ether_types.ETH_TYPE_IP: ip_pkt, proto, data = ipv4.ipv4().parser(pkt) msg += str(ip_pkt) proto_pkt, a, b = proto.parser(data) msg += str(proto_pkt) elif hw_proto == ether_types.ETH_TYPE_IPV6: ip_pkt, proto, data = ipv6.ipv6().parser(pkt) proto_pkt, a, b = proto.parser(data) msg += str(proto_pkt) elif hw_proto == ether_types.ETH_TYPE_ARP: ip_pkt, proto, data = arp.arp().parser(pkt) msg += str(ip_pkt) else: msg += "Does not support hw_proto: " + str(hw_proto) return {'prefix': encodeutils.safe_decode(prefix), 'msg': encodeutils.safe_decode(msg)} class NFLogWrapper(object): """A wrapper for libnetfilter_log api""" _instance = None def __init__(self): self.nflog_g_hanldes = {} @classmethod @runtime.synchronized("nflog-wrapper") def _create_instance(cls): if not cls.has_instance(): cls._instance = cls() @classmethod def has_instance(cls): return cls._instance is not None @classmethod def clear_instance(cls): cls._instance = None @classmethod def get_instance(cls): # double checked locking if not cls.has_instance(): cls._create_instance() return cls._instance def open(self): self.nflog_handle = libnflog.nflog_open() if not self.nflog_handle: msg = _("Could not open nflog handle") raise Exception(msg) self._bind_pf() def close(self): if self.nflog_handle: libnflog.nflog_close(self.nflog_handle) def bind_group(self, group): g_handle = libnflog.nflog_bind_group(self.nflog_handle, group) if g_handle: self.nflog_g_hanldes[group] = g_handle self._set_mode(g_handle, 0x2, 0xffff) self._set_callback(g_handle, self.cb) def _bind_pf(self): for pf in (socket.AF_INET, socket.AF_INET6): libnflog.nflog_unbind_pf(self.nflog_handle, pf) libnflog.nflog_bind_pf(self.nflog_handle, pf) def unbind_group(self, group): try: g_handle = self.nflog_g_hanldes[group] if g_handle: libnflog.nflog_unbind_group(g_handle) except Exception: pass def _set_mode(self, g_handle, mode, len): ret = libnflog.nflog_set_mode(g_handle, mode, len) if ret != 0: msg = _("Could not set mode for nflog") raise Exception(msg) @ffi.callback("nflog_callback") def cb(gh, nfmsg, nfa, data): ev = decode(nfa) msg = jsonutils.dumps(ev) + '\n' ctx = zmq.Context(1) pub = ctx.socket(zmq.XREQ) pub.bind(ADDR_IPC) pub.send(msg.encode('utf-8')) pub.close() return 0 def _set_callback(self, g_handle, cb): ret = libnflog.nflog_callback_register(g_handle, cb, ffi.NULL) if ret != 0: msg = _("Could not set callback for nflog") raise Exception(msg) def run_loop(self): fd = libnflog.nflog_fd(self.nflog_handle) buff = ffi.new('char[]', 4096) while True: try: pkt_len = libnflog.recv(fd, buff, 4096, 0) except OSError as err: # No buffer space available if err.errno == 11: continue msg = _("Unknown exception") raise Exception(msg) if pkt_len > 0: libnflog.nflog_handle_packet(self.nflog_handle, buff, pkt_len) time.sleep(1.0) def start(self): nflog_process = multiprocessing.Process(target=self.run_loop) nflog_process.daemon = True nflog_process.start() return nflog_process.pid @privileged.default.entrypoint def run_nflog(namespace=None, group=0): """Run a nflog process under a namespace This process will listen nflog packets, which are sent from kernel to userspace. Then it decode these packets and send it to IPC address for log application. """ with fwaas_utils.in_namespace(namespace): try: handle = NFLogWrapper.get_instance() handle.open() handle.bind_group(group) pid = handle.start() except Exception: with excutils.save_and_reraise_exception(): LOG.exception("NFLOG thread died of an exception") try: handle.unbind_group(group) handle.close() except Exception: pass return pid class NFLogApp(object): """Log application for handling nflog packets""" callback = None def register_packet_handler(self, caller): self.callback = caller def unregister_packet_handler(self): self.callback = None def start(self): def loop(): while True: if self.callback: ctx = zmq.Context(1) sub = ctx.socket(zmq.XREQ) sub.connect(ADDR_IPC) msg = sub.recv() if len(msg): self.callback(jsonutils.loads(msg)) sub.close() time.sleep(1.0) # Spawn loop eventlet.spawn_n(loop) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/netlink_constants.py0000664000175000017500000000526100000000000025530 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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. # # Some parts are based on python-conntrack: # Copyright (c) 2009-2011,2015 Andrew Grigorev # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # import socket CONNTRACK = 0 NFCT_O_PLAIN = 0 NFCT_OF_TIME_BIT = 1 NFCT_OF_TIME = 1 << NFCT_OF_TIME_BIT NFCT_Q_DESTROY = 2 NFCT_Q_FLUSH = 4 NFCT_Q_DUMP = 5 NFCT_T_DESTROY_BIT = 2 NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT ATTR_IPV4_SRC = 0 ATTR_IPV4_DST = 1 ATTR_IPV6_SRC = 4 ATTR_IPV6_DST = 5 ATTR_PORT_SRC = 8 ATTR_PORT_DST = 9 ATTR_ICMP_TYPE = 12 ATTR_ICMP_CODE = 13 ATTR_ICMP_ID = 14 ATTR_L3PROTO = 15 ATTR_L4PROTO = 17 NFCT_T_NEW_BIT = 0 NFCT_T_NEW = 1 << NFCT_T_NEW_BIT NFCT_T_UPDATE_BIT = 1 NFCT_T_UPDATE = 1 << NFCT_T_UPDATE_BIT NFCT_T_DESTROY_BIT = 2 NFCT_T_DESTROY = 1 << NFCT_T_DESTROY_BIT NFCT_T_ALL = NFCT_T_NEW | NFCT_T_UPDATE | NFCT_T_DESTROY NFCT_CB_CONTINUE = 1 NFCT_CB_FAILURE = -1 NFNL_SUBSYS_CTNETLINK = 0 BUFFER = 1024 # IPv6 address memory buffer ADDR_BUFFER_6 = 16 ADDR_BUFFER_4 = 4 IPVERSION_SOCKET = {4: socket.AF_INET, 6: socket.AF_INET6} IPVERSION_BUFFER = {4: ADDR_BUFFER_4, 6: ADDR_BUFFER_6} ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/netlink_lib.py0000664000175000017500000003121400000000000024257 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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. # # Some parts are based on python-conntrack: # Copyright (c) 2009-2011,2015 Andrew Grigorev # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # import ctypes from ctypes import util from oslo_log import log as logging from neutron_lib import constants from neutron_fwaas import privileged from neutron_fwaas.privileged import netlink_constants as nl_constants from neutron_fwaas.privileged import utils as fwaas_utils LOG = logging.getLogger(__name__) nfct_lib = util.find_library('netfilter_conntrack') nfct = ctypes.CDLL(nfct_lib) libc = ctypes.CDLL(util.find_library('libc.so.6')) # In unit tests the actual nfct library may not be installed, and since we # don't make actual calls to it we don't want to add a hard dependency. if nfct_lib: # It's important that the types be defined properly on all of the functions # we call from nfct, otherwise pointers can be truncated and cause # segfaults. nfct.nfct_set_attr.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p] nfct.nfct_set_attr_u8.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint8] nfct.nfct_set_attr_u16.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_uint16] nfct.nfct_snprintf.argtypes = [ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p, ctypes.c_uint, ctypes.c_uint, ctypes.c_uint] nfct.nfct_new.restype = ctypes.c_void_p nfct.nfct_destroy.argtypes = [ctypes.c_void_p] nfct.nfct_query.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p] nfct.nfct_callback_register.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p] nfct.nfct_open.restype = ctypes.c_void_p nfct.nfct_close.argtypes = [ctypes.c_void_p] IP_VERSIONS = [constants.IP_VERSION_4, constants.IP_VERSION_6] DATA_CALLBACK = None ATTR_POSITIONS = { 'icmp': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)], 'icmpv6': [('type', 6), ('code', 7), ('src', 4), ('dst', 5), ('id', 8)], 'tcp': [('sport', 7), ('dport', 8), ('src', 5), ('dst', 6)], 'udp': [('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)] } TARGET = {'src': {4: nl_constants.ATTR_IPV4_SRC, 6: nl_constants.ATTR_IPV6_SRC}, 'dst': {4: nl_constants.ATTR_IPV4_DST, 6: nl_constants.ATTR_IPV6_DST}, 'ipversion': {4: nl_constants.ATTR_L3PROTO, 6: nl_constants.ATTR_L3PROTO}, 'protocol': {4: nl_constants.ATTR_L4PROTO, 6: nl_constants.ATTR_L4PROTO}, 'code': {4: nl_constants.ATTR_ICMP_CODE, 6: nl_constants.ATTR_ICMP_CODE}, 'type': {4: nl_constants.ATTR_ICMP_TYPE, 6: nl_constants.ATTR_ICMP_TYPE}, 'id': {4: nl_constants.ATTR_ICMP_ID, 6: nl_constants.ATTR_ICMP_ID}, 'sport': {4: nl_constants.ATTR_PORT_SRC, 6: nl_constants.ATTR_PORT_SRC}, 'dport': {4: nl_constants.ATTR_PORT_DST, 6: nl_constants.ATTR_PORT_DST}} NFCT_CALLBACK = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, ctypes.c_void_p, ctypes.c_void_p) class ConntrackOpenFailedExit(SystemExit): """Raised if we fail to open a new conntrack or conntrack handler""" class ConntrackManager(object): def __init__(self, family_socket=None): self.family_socket = family_socket self.set_functions = { 'src': {4: nfct.nfct_set_attr, 6: nfct.nfct_set_attr}, 'dst': {4: nfct.nfct_set_attr, 6: nfct.nfct_set_attr}, 'ipversion': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'protocol': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'type': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'code': {4: nfct.nfct_set_attr_u8, 6: nfct.nfct_set_attr_u8}, 'id': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'sport': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, 'dport': {4: nfct.nfct_set_attr_u16, 6: nfct.nfct_set_attr_u16}, } self.converters = {'src': bytes, 'dst': bytes, 'ipversion': nl_constants.IPVERSION_SOCKET.get, 'protocol': constants.IP_PROTOCOL_MAP.get, 'code': int, 'type': int, 'id': libc.htons, 'sport': libc.htons, 'dport': libc.htons, } def list_entries(self): entries = [] raw_entry = ctypes.create_string_buffer(nl_constants.BUFFER) @NFCT_CALLBACK def callback(type_, conntrack, data): nfct.nfct_snprintf(raw_entry, nl_constants.BUFFER, conntrack, type_, nl_constants.NFCT_O_PLAIN, nl_constants.NFCT_OF_TIME) entries.append(raw_entry.value.decode('utf-8')) return nl_constants.NFCT_CB_CONTINUE self._callback_register(nl_constants.NFCT_T_ALL, callback, DATA_CALLBACK) data_ref = self._get_ref(self.family_socket or nl_constants.IPVERSION_SOCKET[4]) self._query(nl_constants.NFCT_Q_DUMP, data_ref) return entries def delete_entries(self, entries): conntrack = nfct.nfct_new() try: for entry in entries: self._set_attributes(conntrack, entry) self._query(nl_constants.NFCT_Q_DESTROY, conntrack) except Exception as e: msg = "Failed to delete conntrack entries %s" % e LOG.critical(msg) raise ConntrackOpenFailedExit(msg) finally: nfct.nfct_destroy(conntrack) def flush_entries(self): data_ref = self._get_ref(self.family_socket or nl_constants.IPVERSION_SOCKET[4]) self._query(nl_constants.NFCT_Q_FLUSH, data_ref) def _query(self, query_type, query_data): result = nfct.nfct_query(self.conntrack_handler, query_type, query_data) if result == nl_constants.NFCT_CB_FAILURE: LOG.warning("Netlink query failed") def _convert_text_to_binary(self, source, addr_family): dest = ctypes.create_string_buffer( nl_constants.IPVERSION_BUFFER[addr_family]) libc.inet_pton(nl_constants.IPVERSION_SOCKET[addr_family], source.encode('utf-8'), dest) return dest.raw def _set_attributes(self, conntrack, entry): ipversion = entry.get('ipversion', 4) for attr, value in entry.items(): set_function = self.set_functions[attr][ipversion] target = TARGET[attr][ipversion] converter = self.converters[attr] if attr in ['src', 'dst']: # convert src and dst of IPv4 and IPv6 into same format value = self._convert_text_to_binary(value, ipversion) set_function(conntrack, target, converter(value)) def _callback_register(self, message_type, callback_func, data): nfct.nfct_callback_register(self.conntrack_handler, message_type, callback_func, data) def _get_ref(self, data): return ctypes.byref(ctypes.c_int(data)) def __enter__(self): self.conntrack_handler = nfct.nfct_open( nl_constants.CONNTRACK, nl_constants.NFNL_SUBSYS_CTNETLINK) if not self.conntrack_handler: msg = "Failed to open new conntrack handler" LOG.critical(msg) raise ConntrackOpenFailedExit(msg) return self def __exit__(self, *args): nfct.nfct_close(self.conntrack_handler) def _parse_entry(entry, ipversion): """Parse entry from text to Python tuple :param entry: conntrack entry in text :param ipversion: ipversion 4 or 6 :return: conntrack entry in Python tuple example: (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2') The attributes are ordered to be easy to compare with other entries and compare with firewall rule """ protocol = entry[1] parsed_entry = [ipversion, protocol] for attr, position in ATTR_POSITIONS[protocol]: val = entry[position].partition('=')[2] parsed_entry.append(int(val) if attr in ['sport', 'dport', 'type', 'code', 'id'] else val) return tuple(parsed_entry) @privileged.default.entrypoint def flush_entries(namespace=None): """Delete all conntrack entries :param namespace: namespace to delete conntrack entries :return: None """ with fwaas_utils.in_namespace(namespace): for ipversion in IP_VERSIONS: with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \ as conntrack: conntrack.flush_entries() @privileged.default.entrypoint def list_entries(namespace=None): """List and parse all conntrack entries :param namespace: namespace to get conntrack entries :return: sorted list of conntrack entries in Python tuple example: [(4, 'icmp', '8', '0', '1.1.1.1', '2.2.2.2', '1234'), (4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2')] """ parsed_entries = [] with fwaas_utils.in_namespace(namespace): for ipversion in IP_VERSIONS: with ConntrackManager(nl_constants.IPVERSION_SOCKET[ipversion]) \ as conntrack: raw_entries = conntrack.list_entries() for raw_entry in raw_entries: parsed_entry = _parse_entry(raw_entry.split(), ipversion) parsed_entries.append(parsed_entry) return sorted(parsed_entries) @privileged.default.entrypoint def delete_entries(entries, namespace=None): """Delete selected entries :param entries: list of parsed (as tuple) entries to delete :param namespace: namespace to delete conntrack entries :return: None """ entry_args = [] for entry in entries: entry_arg = {'ipversion': entry[0], 'protocol': entry[1]} for idx, attr in enumerate(ATTR_POSITIONS[entry_arg['protocol']]): entry_arg[attr[0]] = entry[idx + 2] entry_args.append(entry_arg) with fwaas_utils.in_namespace(namespace): with ConntrackManager() as conntrack: conntrack.delete_entries(entry_args) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/privileged/tests/0000775000175000017500000000000000000000000022554 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/tests/__init__.py0000664000175000017500000000000000000000000024653 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/privileged/tests/functional/0000775000175000017500000000000000000000000024716 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/tests/functional/__init__.py0000664000175000017500000000000000000000000027015 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/tests/functional/dummy.py0000664000175000017500000000211000000000000026415 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_utils import uuidutils from pyroute2 import netns as pynetns from neutron_fwaas import privileged # TODO(cby): move this method in neutron.tests.functional.privileged associated # to a new privsep context. @privileged.default.entrypoint def dummy(): """This method aim is to validate that we can use privsep in functests.""" namespace = 'dummy-%s' % uuidutils.generate_uuid() pynetns.create(namespace) pynetns.remove(namespace) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/tests/functional/utils.py0000664000175000017500000000233000000000000026426 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import pyroute2 from neutron_fwaas import privileged from neutron_fwaas.privileged import utils def _get_ifname(link): attr_dict = dict(link['attrs']) return attr_dict['IFLA_IFNAME'] def list_interface_names(): iproute = pyroute2.IPRoute() result = iproute.get_links() return [_get_ifname(link) for link in result] @privileged.default.entrypoint def get_in_namespace_interfaces(namespace): before = list_interface_names() with utils.in_namespace(namespace): inside = list_interface_names() after = list_interface_names() return before, inside, after ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/privileged/utils.py0000664000175000017500000000336600000000000023134 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib import os from oslo_log import log as logging from pyroute2 import netns as pynetns from neutron_fwaas._i18n import _ PROCESS_NETNS = '/proc/self/ns/net' LOG = logging.getLogger(__name__) class BackInNamespaceExit(SystemExit): """Raised if we fail to moved back process in its original namespace.""" @contextlib.contextmanager def in_namespace(namespace): """Move current process in a specific namespace. This contextmanager moves current process in a specific namespace and ensures to move it back in original namespace or kills it if we fail to move back in original namespace. """ if not namespace: yield return org_netns_fd = os.open(PROCESS_NETNS, os.O_RDONLY) pynetns.setns(namespace) try: yield finally: try: # NOTE(cby): this code is not executed only if we fail to # move in target namespace pynetns.setns(org_netns_fd) except Exception as e: msg = _('Failed to move back in original netns: %s') % e LOG.critical(msg) raise BackInNamespaceExit(msg) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/services/0000775000175000017500000000000000000000000021103 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/__init__.py0000664000175000017500000000000000000000000023202 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/0000775000175000017500000000000000000000000022710 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/__init__.py0000664000175000017500000000000000000000000025007 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/fwaas_plugin_v2.py0000664000175000017500000004522200000000000026355 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. from neutron.db import servicetype_db as st_db from neutron import service from neutron.services import provider_configuration as provider_conf from neutron.services import service_base from neutron_lib.api.definitions import firewall_v2 from neutron_lib.api.definitions import portbindings as pb_def from neutron_lib.api import validators from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib.callbacks import resources from neutron_lib import constants as nl_constants from neutron_lib.db import api as db_api from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.plugins import constants as plugin_const from neutron_lib.plugins import directory from oslo_log import helpers as log_helpers from oslo_log import log as logging from neutron_fwaas.common import exceptions from neutron_fwaas.common import fwaas_constants from neutron_fwaas.extensions.firewall_v2 import Firewallv2PluginBase from neutron_fwaas.services.firewall.service_drivers import driver_api from neutron_fwaas.services.logapi.agents.drivers.iptables \ import driver as logging_driver LOG = logging.getLogger(__name__) @registry.has_registry_receivers class FirewallPluginV2(Firewallv2PluginBase): """Firewall v2 Neutron service plugin class""" supported_extension_aliases = [firewall_v2.ALIAS] path_prefix = firewall_v2.API_PREFIX def __init__(self): super(FirewallPluginV2, self).__init__() """Do the initialization for the firewall service plugin here.""" # Initialize the Firewall v2 service plugin service_type_manager = st_db.ServiceTypeManager.get_instance() service_type_manager.add_provider_configuration( fwaas_constants.FIREWALL_V2, provider_conf.ProviderConfiguration('neutron_fwaas')) # Load the default driver drivers, default_provider = service_base.load_drivers( fwaas_constants.FIREWALL_V2, self) LOG.info("Firewall v2 Service Plugin using Service Driver: %s", default_provider) if len(drivers) > 1: LOG.warning("Multiple drivers configured for Firewall v2, " "although running multiple drivers in parallel is " "not yet supported") self.driver_name = default_provider self.driver = drivers[default_provider] # start rpc listener if driver required if isinstance(self.driver, driver_api.FirewallDriverRPCMixin): rpc_worker = service.RpcWorker([self], worker_process_count=0) self.add_worker(rpc_worker) log_plugin = directory.get_plugin(plugin_const.LOG_API) logging_driver.register() # If log_plugin was loaded before firewall plugin if log_plugin: # Register logging driver with LoggingServiceDriverManager again log_plugin.driver_manager.register_driver(logging_driver.DRIVER) def start_rpc_listeners(self): return self.driver.start_rpc_listener() @property def _core_plugin(self): return directory.get_plugin() def _ensure_update_firewall_group(self, context, fwg_id): """Checks if the firewall group can be updated Raises FirewallGroupInPendingState if the firewall group is in pending state. :param context: neutron context :param fwg_id: firewall group ID to check :return: Firewall group dict """ fwg = self.get_firewall_group(context, fwg_id) if fwg['status'] in [nl_constants.PENDING_CREATE, nl_constants.PENDING_UPDATE, nl_constants.PENDING_DELETE]: raise f_exc.FirewallGroupInPendingState( firewall_id=fwg_id, pending_state=fwg['status']) return fwg def _ensure_update_firewall_policy(self, context, fwp_id): """Checks if the firewall policy can be updated Fetch firewall group associated to the policy and checks if they can be updated. :param context: neutron context :param fwp_id: firewall policy ID to check """ fwp = self.get_firewall_policy(context, fwp_id) ing_fwg_ids, eg_fwg_ids = self._get_fwgs_with_policy(context, fwp) for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)): self._ensure_update_firewall_group(context, fwg_id) def _ensure_update_firewall_rule(self, context, fwr_id): """Checks if the firewall rule can be updated Fetch firewall policy associated to the rule and checks if they can be updated. :param context: neutron context :param fwr_id: firewall policy ID to check """ fwr = self.get_firewall_rule(context, fwr_id) fwp_ids = self._get_policies_with_rule(context, fwr) for fwp_id in fwp_ids: self._ensure_update_firewall_policy(context, fwp_id) def _validate_firewall_policies_for_firewall_group(self, context, fwg): """Validate firewall group and policy owner Check if the firewall policy is not shared, it have the same project owner than the friewall group. :param context: neutron context :param fwg: firewall group to validate """ for policy_type in ['ingress_firewall_policy_id', 'egress_firewall_policy_id']: if fwg.get(policy_type): fwp = self.get_firewall_policy(context, fwg[policy_type]) if fwg['tenant_id'] != fwp['tenant_id'] and not fwp['shared']: raise f_exc.FirewallPolicyConflict( firewall_policy_id=fwg[policy_type]) def _validate_ports_for_firewall_group(self, context, tenant_id, fwg_ports): """Validate firewall group associated ports Check if the firewall group associated ports have the same project owner and is router interface type or a compute layer 2 and supported by the firewall driver :param context: neutron context :param tenant_id: firewall group project ID :param fwg_ports: firewall group associated ports """ # TODO(sridar): elevated context and do we want to use public ? for port_id in fwg_ports: port = self._core_plugin.get_port(context, port_id) if port['tenant_id'] != tenant_id: raise f_exc.FirewallGroupPortInvalidProject( port_id=port_id, project_id=port['tenant_id']) device_owner = port.get('device_owner', '') if device_owner in nl_constants.ROUTER_INTERFACE_OWNERS: if not self.driver.is_supported_l3_port(port): raise exceptions.FirewallGroupPortNotSupported( driver_name=self.driver_name, port_id=port_id) elif device_owner.startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): if not self._is_supported_l2_port(context, port_id): raise exceptions.FirewallGroupPortNotSupported( driver_name=self.driver_name, port_id=port_id) else: raise f_exc.FirewallGroupPortInvalid(port_id=port_id) def _is_supported_l2_port(self, context, port_id): """Whether this l2 port is supported""" # Re-fetch to get up-to-date data from db port = self._core_plugin.get_port(context, id=port_id) # Skip port binding is unbound or failed if port[pb_def.VIF_TYPE] in [pb_def.VIF_TYPE_UNBOUND, pb_def.VIF_TYPE_BINDING_FAILED]: return False return self.driver.is_supported_l2_port(port) def _validate_if_firewall_group_on_ports(self, context, firewall_group, id=None): """Validate if ports are not associated with any firewall_group. If any of the ports in the list is already associated with a firewall group, raise an exception else just return. :param context: neutron context :param fwg: firewall group to validate """ if 'ports' not in firewall_group or not firewall_group['ports']: return filters = { 'tenant_id': [firewall_group['tenant_id']], 'ports': firewall_group['ports'], } ports_in_use = set() for fwg in self.get_firewall_groups(context, filters=filters): if id is not None and fwg['id'] == id: continue ports_in_use |= set(fwg.get('ports', [])) & \ set(firewall_group['ports']) if ports_in_use: raise f_exc.FirewallGroupPortInUse(port_ids=list(ports_in_use)) def _get_fwgs_with_policy(self, context, firewall_policy): """List firewall group IDs which use a firewall policy List all firewall group IDs which have the given firewall policy as ingress or egress. :param context: neutron context :param firewall_policy: firewall policy to filter """ filters = { 'tenant_id': [firewall_policy['tenant_id']], 'ingress_firewall_policy_id': [firewall_policy['id']], } ingress_fwp_ids = [fwg['id'] for fwg in self.get_firewall_groups( context, filters=filters)] filters = { 'tenant_id': [firewall_policy['tenant_id']], 'egress_firewall_policy_id': [firewall_policy['id']], } egress_fwp_ids = [fwg['id'] for fwg in self.get_firewall_groups( context, filters=filters)] return ingress_fwp_ids, egress_fwp_ids def _get_policies_with_rule(self, context, firewall_rule): filters = { 'tenant_id': [firewall_rule['tenant_id']], 'firewall_rules': [firewall_rule['id']], } return [fwp['id'] for fwp in self.get_firewall_policies( context, filters=filters)] def _validate_insert_remove_rule_request(self, rule_info): """Validate rule_info dict Check that all mandatory fields are present, otherwise raise proper exception. """ if not rule_info or 'firewall_rule_id' not in rule_info: raise f_exc.FirewallRuleInfoMissing() # Validator doesn't return anything if the check passes if validators.validate_uuid(rule_info['firewall_rule_id']): raise f_exc.FirewallRuleNotFound( firewall_rule_id=rule_info['firewall_rule_id']) @registry.receives(resources.PORT, [events.AFTER_UPDATE]) def handle_update_port(self, resource, event, trigger, payload): context = payload.context original_port = payload.states[0] updated_port = payload.states[1] if not updated_port['device_owner'].startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): return if (original_port[pb_def.VIF_TYPE] != pb_def.VIF_TYPE_UNBOUND): # Checking newly vm port binding allows us to avoid call to DB # when a port update_event like restart, setting name, etc... # Moreover, that will help us in case of tenant admin wants to # only attach security group to vm port. return port_id = updated_port['id'] # Check port is supported by firewall driver if not self._is_supported_l2_port(context, port_id): return project_id = updated_port['project_id'] fwgs = self.get_firewall_groups( context, filters={ 'tenant_id': [project_id], 'name': [fwaas_constants.DEFAULT_FWG], }, fields=['id', 'ports'], ) if len(fwgs) != 1: # Cannot found default Firewall Group, abandon LOG.warning("Cannot found default firewall group of project %s", project_id) return default_fwg = fwgs[0] # Add default firewall group to the port port_ids = default_fwg.get('ports', []) + [port_id] try: self.update_firewall_group(context, default_fwg['id'], {'firewall_group': {'ports': port_ids}}) except f_exc.FirewallGroupPortInUse: LOG.warning("Port %s has been already associated with default " "firewall group %s and skip association", port_id, default_fwg['id']) # Firewall Group @log_helpers.log_method_call @db_api.CONTEXT_WRITER def create_firewall_group(self, context, firewall_group): firewall_group = firewall_group['firewall_group'] ports = firewall_group.get('ports', []) self._validate_firewall_policies_for_firewall_group(context, firewall_group) # Validate ports owner type and project self._validate_ports_for_firewall_group(context, firewall_group['tenant_id'], ports) self._validate_if_firewall_group_on_ports(context, firewall_group) return self.driver.create_firewall_group(context, firewall_group) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def delete_firewall_group(self, context, id): # if no such group exists -> don't raise an exception according to # 80fe2ba1, return None try: fwg = self.get_firewall_group(context, id) except f_exc.FirewallGroupNotFound: return if fwg['ports']: raise f_exc.FirewallGroupInUse(firewall_id=id) self.driver.delete_firewall_group(context, id) @log_helpers.log_method_call @db_api.CONTEXT_READER def get_firewall_group(self, context, id, fields=None): return self.driver.get_firewall_group(context, id, fields=fields) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def get_firewall_groups(self, context, filters=None, fields=None): return self.driver.get_firewall_groups(context, filters, fields) @log_helpers.log_method_call def get_firewall_groups_count(self, context, filters=None): filters = filters or {} return len(self.get_firewall_groups(context=context, filters=filters)) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def update_firewall_group(self, context, id, firewall_group): firewall_group = firewall_group['firewall_group'] ports = firewall_group.get('ports', []) old_firewall_group = self._ensure_update_firewall_group(context, id) firewall_group['tenant_id'] = old_firewall_group['tenant_id'] self._validate_firewall_policies_for_firewall_group(context, firewall_group) # Validate ports owner type and project self._validate_ports_for_firewall_group(context, firewall_group['tenant_id'], ports) self._validate_if_firewall_group_on_ports(context, firewall_group, id=id) return self.driver.update_firewall_group(context, id, firewall_group) # Firewall Policy @log_helpers.log_method_call @db_api.CONTEXT_WRITER def create_firewall_policy(self, context, firewall_policy): firewall_policy = firewall_policy['firewall_policy'] return self.driver.create_firewall_policy(context, firewall_policy) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def delete_firewall_policy(self, context, id): self.driver.delete_firewall_policy(context, id) @log_helpers.log_method_call @db_api.CONTEXT_READER def get_firewall_policy(self, context, id, fields=None): return self.driver.get_firewall_policy(context, id, fields) @log_helpers.log_method_call @db_api.CONTEXT_READER def get_firewall_policies(self, context, filters=None, fields=None): return self.driver.get_firewall_policies(context, filters, fields) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def update_firewall_policy(self, context, id, firewall_policy): firewall_policy = firewall_policy['firewall_policy'] self._ensure_update_firewall_policy(context, id) return self.driver.update_firewall_policy(context, id, firewall_policy) # Firewall Rule @log_helpers.log_method_call @db_api.CONTEXT_WRITER def create_firewall_rule(self, context, firewall_rule): firewall_rule = firewall_rule['firewall_rule'] return self.driver.create_firewall_rule(context, firewall_rule) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def delete_firewall_rule(self, context, id): self.driver.delete_firewall_rule(context, id) @log_helpers.log_method_call @db_api.CONTEXT_READER def get_firewall_rule(self, context, id, fields=None): return self.driver.get_firewall_rule(context, id, fields) @log_helpers.log_method_call @db_api.CONTEXT_READER def get_firewall_rules(self, context, filters=None, fields=None): return self.driver.get_firewall_rules(context, filters, fields) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def update_firewall_rule(self, context, id, firewall_rule): firewall_rule = firewall_rule['firewall_rule'] self._ensure_update_firewall_rule(context, id) return self.driver.update_firewall_rule(context, id, firewall_rule) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def insert_rule(self, context, policy_id, rule_info): self._ensure_update_firewall_policy(context, policy_id) self._validate_insert_remove_rule_request(rule_info) return self.driver.insert_rule(context, policy_id, rule_info) @log_helpers.log_method_call @db_api.CONTEXT_WRITER def remove_rule(self, context, policy_id, rule_info): self._ensure_update_firewall_policy(context, policy_id) self._validate_insert_remove_rule_request(rule_info) return self.driver.remove_rule(context, policy_id, rule_info) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3013506 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/0000775000175000017500000000000000000000000026106 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/__init__.py0000664000175000017500000000000000000000000030205 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3053508 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/0000775000175000017500000000000000000000000027367 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/__init__.py0000664000175000017500000000000000000000000031466 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/agents.py0000664000175000017500000003767000000000000031237 0ustar00zuulzuul00000000000000# Copyright (c) 2013 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. from neutron_lib.api.definitions import portbindings as pb_def from neutron_lib import constants as nl_constants from neutron_lib import context as neutron_context from neutron_lib.db import api as db_api from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib import rpc as n_rpc from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging import oslo_messaging from neutron_fwaas.common import fwaas_constants as constants from neutron_fwaas.services.firewall.service_drivers import driver_api LOG = logging.getLogger(__name__) class FirewallAgentCallbacks(object): target = oslo_messaging.Target(version='1.0') def __init__(self, firewall_db): self.firewall_db = firewall_db @log_helpers.log_method_call @db_api.CONTEXT_WRITER def set_firewall_group_status(self, context, fwg_id, status, **kwargs): """Agent uses this to set a firewall_group's status.""" # Sanitize status first if status in (nl_constants.ACTIVE, nl_constants.DOWN, nl_constants.INACTIVE): to_update = status else: to_update = nl_constants.ERROR # ignore changing status if firewall_group expects to be deleted # That case means that while some pending operation has been # performed on the backend, neutron server received delete request # and changed firewall status to PENDING_DELETE updated = self.firewall_db.update_firewall_group_status( context, fwg_id, to_update, not_in=(nl_constants.PENDING_DELETE,)) if updated: LOG.debug("firewall %s status set: %s", fwg_id, to_update) return updated and to_update != nl_constants.ERROR @log_helpers.log_method_call @db_api.CONTEXT_WRITER def firewall_group_deleted(self, context, fwg_id, **kwargs): """Agent uses this to indicate firewall is deleted.""" try: fwg = self.firewall_db.get_firewall_group(context, fwg_id) # allow to delete firewalls in ERROR state if fwg['status'] in (nl_constants.PENDING_DELETE, nl_constants.ERROR): self.firewall_db.delete_firewall_group(context, fwg_id) return True LOG.warning('Firewall %(fwg)s unexpectedly deleted by agent, ' 'status was %(status)s', {'fwg': fwg_id, 'status': fwg['status']}) fwg['status'] = nl_constants.ERROR self.firewall_db.update_firewall_group(context, fwg_id, fwg) return False except f_exc.FirewallGroupNotFound: LOG.info('Firewall group %s already deleted', fwg_id) return True @log_helpers.log_method_call @db_api.CONTEXT_WRITER def get_firewall_groups_for_project(self, context, **kwargs): """Gets all firewall_groups and rules on a project.""" fwg_list = [] for fwg in self.firewall_db.get_firewall_groups(context): fwg_with_rules =\ self.firewall_db.make_firewall_group_dict_with_rules( context, fwg['id']) if fwg['status'] == nl_constants.PENDING_DELETE: fwg_with_rules['add-port-ids'] = [] fwg_with_rules['del-port-ids'] = ( self.firewall_db.get_ports_in_firewall_group( context, fwg['id'])) else: fwg_with_rules['add-port-ids'] = ( self.firewall_db.get_ports_in_firewall_group( context, fwg['id'])) fwg_with_rules['del-port-ids'] = [] fwg_list.append(fwg_with_rules) return fwg_list @log_helpers.log_method_call @db_api.CONTEXT_WRITER def get_projects_with_firewall_groups(self, context, **kwargs): """Get all projects that have firewall_groups.""" ctx = neutron_context.get_admin_context() fwg_list = self.firewall_db.get_firewall_groups(ctx) fwg_project_list = list(set(fwg['tenant_id'] for fwg in fwg_list)) return fwg_project_list @log_helpers.log_method_call @db_api.CONTEXT_WRITER def get_firewall_group_for_port(self, context, **kwargs): """Get firewall_group is associated with a port.""" ctx = context.elevated() # Only one Firewall Group can be associated to a port at a time fwg_port_binding = self.firewall_db.get_firewall_groups( ctx, filters={'ports': [kwargs.get('port_id')]}) if len(fwg_port_binding) != 1: return fwg = fwg_port_binding[0] fwg['ingress_rule_list'] = [] for rule_id in self.firewall_db.get_firewall_policy( context, fwg['ingress_firewall_policy_id'], fields=['firewall_rules'])['firewall_rules']: fwg['ingress_rule_list'].append( self.firewall_db.get_firewall_rule(context, rule_id)) fwg['egress_rule_list'] = [] for rule_id in self.firewall_db.get_firewall_policy( context, fwg['egress_firewall_policy_id'], fields=['firewall_rules'])['firewall_rules']: fwg['egress_rule_list'].append( self.firewall_db.get_firewall_rule(context, rule_id)) return fwg class FirewallAgentApi(object): """Plugin side of plugin to agent RPC API""" def __init__(self, topic, host): self.host = host target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def create_firewall_group(self, context, firewall_group): cctxt = self.client.prepare(fanout=True) cctxt.cast(context, 'create_firewall_group', firewall_group=firewall_group, host=self.host) def update_firewall_group(self, context, firewall_group): cctxt = self.client.prepare(fanout=True) cctxt.cast(context, 'update_firewall_group', firewall_group=firewall_group, host=self.host) def delete_firewall_group(self, context, firewall_group): cctxt = self.client.prepare(fanout=True) cctxt.cast(context, 'delete_firewall_group', firewall_group=firewall_group, host=self.host) class FirewallAgentDriver(driver_api.FirewallDriverDB, driver_api.FirewallDriverRPCMixin): """Firewall driver to implement agent messages and callback methods Implement RPC Firewall v2 API and callback methods for agents based on Neutron DB model. """ def __init__(self, service_plugin): super(FirewallAgentDriver, self).__init__(service_plugin) self.agent_rpc = FirewallAgentApi(constants.FW_AGENT, cfg.CONF.host) def is_supported_l2_port(self, port): if port[pb_def.VIF_TYPE] == pb_def.VIF_TYPE_OVS: if not port['port_security_enabled']: return True # TODO(annp): remove these lines after we fully support for hybrid # port if not port[pb_def.VIF_DETAILS][pb_def.OVS_HYBRID_PLUG]: return True LOG.warning("Doesn't support hybrid port at the moment") else: LOG.warning("Doesn't support vif type %s", port[pb_def.VIF_TYPE]) return False def is_supported_l3_port(self, port): return True def start_rpc_listener(self): self.endpoints = [FirewallAgentCallbacks(self.firewall_db)] self.rpc_connection = n_rpc.Connection() self.rpc_connection.create_consumer(constants.FIREWALL_PLUGIN, self.endpoints, fanout=False) return self.rpc_connection.consume_in_threads() def _rpc_update_firewall_group(self, context, fwg_id): fw_ports = self.firewall_db.get_ports_in_firewall_group( context, fwg_id) if not fw_ports: return status_update = {"status": nl_constants.PENDING_UPDATE} self.update_firewall_group(context, fwg_id, status_update) fwg_with_rules = self.firewall_db.make_firewall_group_dict_with_rules( context, fwg_id) # this is triggered on an update to fwg rule or policy, no # change in associated ports. fwg_with_rules['add-port-ids'] = fw_ports fwg_with_rules['del-port-ids'] = [] fwg_with_rules['port_details'] = self._get_fwg_port_details( context, fwg_with_rules['add-port-ids']) self.agent_rpc.update_firewall_group(context, fwg_with_rules) def _rpc_update_firewall_policy(self, context, firewall_policy_id): firewall_policy = self.get_firewall_policy(context, firewall_policy_id) if firewall_policy: ing_fwg_ids, eg_fwg_ids = self.firewall_db.get_fwgs_with_policy( context, firewall_policy_id) for fwg_id in list(set(ing_fwg_ids + eg_fwg_ids)): self._rpc_update_firewall_group(context, fwg_id) def _get_fwg_port_details(self, context, fwg_ports): """Returns a dictionary list of port details. """ port_details = {} for port_id in fwg_ports: port_db = self._core_plugin.get_port(context, port_id) # Add more parameters below based on requirement. device_owner = port_db['device_owner'] port_details[port_id] = { 'device_owner': device_owner, 'device': port_db['id'], 'network_id': port_db['network_id'], 'fixed_ips': port_db['fixed_ips'], 'allowed_address_pairs': port_db.get('allowed_address_pairs', []), 'port_security_enabled': port_db.get('port_security_enabled', True), 'id': port_db['id'], 'status': port_db['status'], } if device_owner.startswith( nl_constants.DEVICE_OWNER_COMPUTE_PREFIX): port_details[port_id].update( {'host': port_db[pb_def.HOST_ID]}) return port_details def create_firewall_group_precommit(self, context, firewall_group): ports = firewall_group['ports'] if (not ports or (not firewall_group['ingress_firewall_policy_id'] and not firewall_group['egress_firewall_policy_id'])): # no messaging to agent needed and fw needs to go to INACTIVE state # as no associated ports and/or no policy configured. status = nl_constants.INACTIVE else: status = (nl_constants.CREATED if cfg.CONF.router_distributed else nl_constants.PENDING_CREATE) firewall_group['status'] = status def create_firewall_group_postcommit(self, context, firewall_group): if firewall_group['status'] != nl_constants.INACTIVE: fwg_with_rules =\ self.firewall_db.make_firewall_group_dict_with_rules( context, firewall_group['id']) fwg_with_rules['add-port-ids'] = firewall_group['ports'] fwg_with_rules['del-ports-id'] = [] fwg_with_rules['port_details'] = self._get_fwg_port_details( context, firewall_group['ports']) self.agent_rpc.create_firewall_group(context, fwg_with_rules) def _need_pending_update(self, old_firewall_group, new_firewall_group): port_updated = (set(new_firewall_group['ports']) != set(old_firewall_group['ports'])) policies_updated = ( new_firewall_group['ingress_firewall_policy_id'] != old_firewall_group['ingress_firewall_policy_id'] or new_firewall_group['egress_firewall_policy_id'] != old_firewall_group['egress_firewall_policy_id'] ) if (port_updated and (new_firewall_group['ingress_firewall_policy_id'] or new_firewall_group['egress_firewall_policy_id'])): return True if policies_updated and new_firewall_group['ports']: return True return False def update_firewall_group_precommit(self, context, old_firewall_group, new_firewall_group): if self._need_pending_update(old_firewall_group, new_firewall_group): new_firewall_group['status'] = nl_constants.PENDING_UPDATE def update_firewall_group_postcommit(self, context, old_firewall_group, new_firewall_group): if not self._need_pending_update(old_firewall_group, new_firewall_group): return fwg_with_rules = self.firewall_db.make_firewall_group_dict_with_rules( context, new_firewall_group['id']) # determine ports to add fw to and del from fwg_with_rules['add-port-ids'] = list( set(new_firewall_group['ports']) - set(old_firewall_group['ports']) ) fwg_with_rules['del-port-ids'] = list( set(old_firewall_group['ports']) - set(new_firewall_group['ports']) ) # last-port drives agent to ack with status to set state to INACTIVE # Set last-port to True if there are no ports in the new group and # the old group had the same number of ports that need to be deleted. fwg_with_rules['last-port'] = (len(old_firewall_group['ports']) == len( fwg_with_rules['del-port-ids']) and not new_firewall_group['ports']) LOG.debug("update_firewall_group %s: Add Ports: %s, Del Ports: %s", new_firewall_group['id'], fwg_with_rules['add-port-ids'], fwg_with_rules['del-port-ids']) fwg_with_rules['port_details'] = self._get_fwg_port_details( context, fwg_with_rules['del-port-ids']) fwg_with_rules['port_details'].update(self._get_fwg_port_details( context, fwg_with_rules['add-port-ids'])) if (new_firewall_group['name'] == constants.DEFAULT_FWG and len(fwg_with_rules['add-port-ids']) == 1 and not fwg_with_rules['del-port-ids']): port_id = fwg_with_rules['add-port-ids'][0] if (fwg_with_rules['port_details'][port_id].get('status') != nl_constants.ACTIVE): # If port not yet active, just associate to default firewall # group. When agent will set it to UP, it'll found FG # association and enforce default policies return # Warn agents Firewall Group port list updated self.agent_rpc.update_firewall_group(context, fwg_with_rules) def update_firewall_policy_postcommit(self, context, old_firewall_policy, new_firewall_group): self._rpc_update_firewall_policy(context, new_firewall_group['id']) def update_firewall_rule_postcommit(self, context, old_firewall_rule, new_firewall_rule): firewall_policy_ids = self.firewall_db.get_policies_with_rule( context, new_firewall_rule['id']) for firewall_policy_id in firewall_policy_ids: self._rpc_update_firewall_policy(context, firewall_policy_id) def insert_rule_postcommit(self, context, policy_id, rule_info): self._rpc_update_firewall_policy(context, policy_id) def remove_rule_postcommit(self, context, policy_id, rule_info): self._rpc_update_firewall_policy(context, policy_id) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3053508 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/0000775000175000017500000000000000000000000031045 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/__init__.py0000664000175000017500000000000000000000000033144 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/conntrack_base.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/conntrack_base.p0000664000175000017500000000336300000000000034207 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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_lib.utils import runtime from oslo_config import cfg from oslo_log import log as logging from oslo_utils import excutils LOG = logging.getLogger(__name__) def load_and_init_conntrack_driver(*args, **kwargs): driver = cfg.CONF.fwaas.conntrack_driver try: conntrack_driver_cls = runtime.load_class_by_alias_or_classname( 'neutron.agent.l3.firewall_drivers', driver) except ImportError: with excutils.save_and_reraise_exception(): LOG.exception("Driver '%s' not found.", driver) conntrack_driver = conntrack_driver_cls() conntrack_driver.initialize(*args, **kwargs) return conntrack_driver class ConntrackDriverBase(object, metaclass=abc.ABCMeta): """Base Driver for Conntrack""" @abc.abstractmethod def initialize(self, *args, **kwargs): """Initialize the driver""" @abc.abstractmethod def delete_entries(self, rules, namespace): """Delete conntrack entries specified by list of rules""" @abc.abstractmethod def flush_entries(self, namespace): """Delete all conntrack entries within namespace""" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base.py0000664000175000017500000001032700000000000033515 0ustar00zuulzuul00000000000000# Copyright 2013 Dell Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc class FwaasDriverBase(object, metaclass=abc.ABCMeta): """Firewall as a Service Driver base class. Using FwaasDriver Class, an instance of L3 perimeter Firewall can be created. The firewall co-exists with the L3 agent. One instance is created for each tenant. One firewall policy is associated with each tenant (in the Havana release). The Firewall can be visualized as having two zones (in Havana release), trusted and untrusted. All the 'internal' interfaces of Neutron Router is treated as trusted. The interface connected to 'external network' is treated as untrusted. The policy is applied on traffic ingressing/egressing interfaces on the trusted zone. This implies that policy will be applied for traffic passing from - trusted to untrusted zones - untrusted to trusted zones - trusted to trusted zones Policy WILL NOT be applied for traffic from untrusted to untrusted zones. This is not a problem in Havana release as there is only one interface connected to external network. Since the policy is applied on the internal interfaces, the traffic will be not be NATed to floating IP. For incoming traffic, the traffic will get NATed to internal IP address before it hits the firewall rules. So, while writing the rules, care should be taken if using rules based on floating IP. The firewall rule addition/deletion/insertion/update are done by the management console. When the policy is sent to the driver, the complete policy is sent and the whole policy has to be applied atomically. The firewall rules will not get updated individually. This is to avoid problems related to out-of-order notifications or inconsistent behaviour by partial application of rules. Argument agent_mode indicates the l3 agent in DVR or DVR_SNAT or LEGACY mode. """ # TODO(Margaret): Remove the first 3 methods and make the second three # @abc.abstractmethod def create_firewall(self, agent_mode, apply_list, firewall): """Create the Firewall with default (drop all) policy. The default policy will be applied on all the interfaces of trusted zone. """ pass def delete_firewall(self, agent_mode, apply_list, firewall): """Delete firewall. Removes all policies created by this instance and frees up all the resources. """ pass def update_firewall(self, agent_mode, apply_list, firewall): """Apply the policy on all trusted interfaces. Remove previous policy and apply the new policy on all trusted interfaces. """ pass def create_firewall_group(self, agent_mode, apply_list, firewall): """Create the Firewall with default (drop all) policy. The default policy will be applied on all the interfaces of trusted zone. """ pass def delete_firewall_group(self, agent_mode, apply_list, firewall): """Delete firewall. Removes all policies created by this instance and frees up all the resources. """ pass def update_firewall_group(self, agent_mode, apply_list, firewall): """Apply the policy on all trusted interfaces. Remove previous policy and apply the new policy on all trusted interfaces. """ pass @abc.abstractmethod def apply_default_policy(self, agent_mode, apply_list, firewall): """Apply the default policy on all trusted interfaces. Remove current policy and apply the default policy on all trusted interfaces. """ pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base_v2.py0000664000175000017500000000677300000000000034136 0ustar00zuulzuul00000000000000# Copyright (c) 2016 # 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 class FwaasDriverBase(object, metaclass=abc.ABCMeta): """Firewall as a Service Driver base class. Using FwaasDriver Class, an instance of L3 perimeter Firewall can be created. The firewall co-exists with the L3 agent. One instance is created for each tenant. One firewall policy is associated with each tenant (in the Havana release). The Firewall can be visualized as having two zones (in Havana release), trusted and untrusted. All the 'internal' interfaces of Neutron Router is treated as trusted. The interface connected to 'external network' is treated as untrusted. The policy is applied on traffic ingressing/egressing interfaces on the trusted zone. This implies that policy will be applied for traffic passing from - trusted to untrusted zones - untrusted to trusted zones - trusted to trusted zones Policy WILL NOT be applied for traffic from untrusted to untrusted zones. This is not a problem in Havana release as there is only one interface connected to external network. Since the policy is applied on the internal interfaces, the traffic will be not be NATed to floating IP. For incoming traffic, the traffic will get NATed to internal IP address before it hits the firewall rules. So, while writing the rules, care should be taken if using rules based on floating IP. The firewall rule addition/deletion/insertion/update are done by the management console. When the policy is sent to the driver, the complete policy is sent and the whole policy has to be applied atomically. The firewall rules will not get updated individually. This is to avoid problems related to out-of-order notifications or inconsistent behaviour by partial application of rules. Argument agent_mode indicates the l3 agent in DVR or DVR_SNAT or LEGACY mode. """ @abc.abstractmethod def create_firewall_group(self, agent_mode, apply_list, firewall): """Create the Firewall with default (drop all) policy. The default policy will be applied on all the interfaces of trusted zone. """ pass @abc.abstractmethod def delete_firewall_group(self, agent_mode, apply_list, firewall): """Delete firewall. Removes all policies created by this instance and frees up all the resources. """ pass @abc.abstractmethod def update_firewall_group(self, agent_mode, apply_list, firewall): """Apply the policy on all trusted interfaces. Remove previous policy and apply the new policy on all trusted interfaces. """ pass @abc.abstractmethod def apply_default_policy(self, agent_mode, apply_list, firewall): """Apply the default policy on all trusted interfaces. Remove current policy and apply the default policy on all trusted interfaces. """ pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3053508 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/0000775000175000017500000000000000000000000032204 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/__init__.p0000664000175000017500000000000000000000000034112 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas_v2.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_f0000664000175000017500000005667500000000000034262 0ustar00zuulzuul00000000000000# Copyright (c) 2016 # 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.agent.linux import iptables_manager from neutron.common import utils from neutron_lib import constants from neutron_lib.exceptions import firewall_v2 as fw_ext from oslo_log import log as logging from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ conntrack_base from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ fwaas_base_v2 LOG = logging.getLogger(__name__) FWAAS_DRIVER_NAME = 'Fwaas iptables driver' FWAAS_DEFAULT_CHAIN = 'fwaas-default-policy' # Introduce these chain for future processing like firewall logging ACCEPTED_CHAIN = 'accepted' DROPPED_CHAIN = 'dropped' REJECTED_CHAIN = 'rejected' FWAAS_TO_IPTABLE_ACTION_MAP = { 'allow': ACCEPTED_CHAIN, 'deny': DROPPED_CHAIN, 'reject': REJECTED_CHAIN } CHAIN_NAME_PREFIX = {constants.INGRESS_DIRECTION: 'i', constants.EGRESS_DIRECTION: 'o'} """ Firewall rules are applied on internal-interfaces of Neutron router. The packets ingressing tenant's network will be on the output direction on internal-interfaces. """ IPTABLES_DIR = {constants.INGRESS_DIRECTION: '-o', constants.EGRESS_DIRECTION: '-i'} IPV4 = 'ipv4' IPV6 = 'ipv6' IP_VER_TAG = {IPV4: 'v4', IPV6: 'v6'} INTERNAL_DEV_PREFIX = 'qr-' SNAT_INT_DEV_PREFIX = 'sg-' ROUTER_2_FIP_DEV_PREFIX = 'rfp-' MAX_INTF_NAME_LEN = 14 class IptablesFwaasDriver(fwaas_base_v2.FwaasDriverBase): """IPTables driver for Firewall As A Service.""" def __init__(self): LOG.debug("Initializing fwaas iptables driver") self.pre_firewall = None self.conntrack = conntrack_base.load_and_init_conntrack_driver() def _get_intf_name(self, if_prefix, port_id): _name = "%s%s" % (if_prefix, port_id) return _name[:MAX_INTF_NAME_LEN] def create_firewall_group(self, agent_mode, apply_list, firewall): LOG.debug('Creating firewall %(fw_id)s for tenant %(tid)s', {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) try: if firewall['admin_state_up']: self._setup_firewall(agent_mode, apply_list, firewall) self._remove_conntrack_new_firewall(agent_mode, apply_list, firewall) self.pre_firewall = dict(firewall) else: self.apply_default_policy(agent_mode, apply_list, firewall) except (LookupError, RuntimeError): # catch known library exceptions and raise Fwaas generic exception LOG.exception("Failed to create firewall: %s", firewall['id']) raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) def _get_ipt_mgrs_with_if_prefix(self, agent_mode, ri): """Gets the iptables manager along with the if prefix to apply rules. With DVR we can have differing namespaces depending on which agent (on Network or Compute node). Also, there is an associated i/f for each namespace. The iptables on the relevant namespace and matching i/f are provided. On the Network node we could have both the snat namespace and a fip so this is provided back as a list - so in that scenario rules can be applied on both. """ if not ri.router.get('distributed'): return [{'ipt': ri.iptables_manager, 'if_prefix': INTERNAL_DEV_PREFIX}] ipt_mgrs = [] # TODO(sridar): refactor to get strings to a common location. if agent_mode == 'dvr_snat': if ri.snat_iptables_manager: ipt_mgrs.append({'ipt': ri.snat_iptables_manager, 'if_prefix': SNAT_INT_DEV_PREFIX}) if ri.rtr_fip_connect: # handle the fip case on n/w or compute node. ipt_mgrs.append({'ipt': ri.iptables_manager, 'if_prefix': ROUTER_2_FIP_DEV_PREFIX}) return ipt_mgrs def delete_firewall_group(self, agent_mode, apply_list, firewall): LOG.debug('Deleting firewall %(fw_id)s for tenant %(tid)s', {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) fwid = firewall['id'] try: for ri, router_fw_ports in apply_list: ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix( agent_mode, ri) for ipt_if_prefix in ipt_if_prefix_list: ipt_mgr = ipt_if_prefix['ipt'] self._remove_chains(fwid, ipt_mgr) self._remove_default_chains(ipt_mgr) # apply the changes immediately (no defer in firewall path) ipt_mgr.defer_apply_off() self.pre_firewall = None except (LookupError, RuntimeError): # catch known library exceptions and raise Fwaas generic exception LOG.exception("Failed to delete firewall: %s", fwid) raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) def update_firewall_group(self, agent_mode, apply_list, firewall): LOG.debug('Updating firewall %(fw_id)s for tenant %(tid)s', {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) try: if firewall['admin_state_up']: self._setup_firewall(agent_mode, apply_list, firewall) if self.pre_firewall: self._remove_conntrack_updated_firewall(agent_mode, apply_list, self.pre_firewall, firewall) else: self._remove_conntrack_new_firewall(agent_mode, apply_list, firewall) else: self.apply_default_policy(agent_mode, apply_list, firewall) self.pre_firewall = dict(firewall) except (LookupError, RuntimeError): # catch known library exceptions and raise Fwaas generic exception LOG.exception("Failed to update firewall: %s", firewall['id']) raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) def apply_default_policy(self, agent_mode, apply_list, firewall): LOG.debug('Applying firewall %(fw_id)s for tenant %(tid)s', {'fw_id': firewall['id'], 'tid': firewall['tenant_id']}) fwid = firewall['id'] try: for ri, router_fw_ports in apply_list: ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix( agent_mode, ri) for ipt_if_prefix in ipt_if_prefix_list: # the following only updates local memory; no hole in FW ipt_mgr = ipt_if_prefix['ipt'] self._remove_chains(fwid, ipt_mgr) self._remove_default_chains(ipt_mgr) # Create accepted/dropped/rejected chain self._add_accepted_chain_v4v6(ipt_mgr) self._add_dropped_chain_v4v6(ipt_mgr) self._add_rejected_chain_v4v6(ipt_mgr) # create default 'DROP ALL' policy chain self._add_default_policy_chain_v4v6(ipt_mgr) self._enable_policy_chain(fwid, ipt_if_prefix, router_fw_ports) # apply the changes immediately (no defer in firewall path) ipt_mgr.defer_apply_off() except (LookupError, RuntimeError): # catch known library exceptions and raise Fwaas generic exception LOG.exception( "Failed to apply default policy on firewall: %s", fwid) raise fw_ext.FirewallInternalDriverError(driver=FWAAS_DRIVER_NAME) def _setup_firewall(self, agent_mode, apply_list, firewall): fwid = firewall['id'] for ri, router_fw_ports in apply_list: ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix( agent_mode, ri) for ipt_if_prefix in ipt_if_prefix_list: ipt_mgr = ipt_if_prefix['ipt'] # the following only updates local memory; no hole in FW self._remove_chains(fwid, ipt_mgr) self._remove_default_chains(ipt_mgr) # Create accepted/dropped/rejected chain self._add_accepted_chain_v4v6(ipt_mgr) self._add_dropped_chain_v4v6(ipt_mgr) self._add_rejected_chain_v4v6(ipt_mgr) # create default 'DROP ALL' policy chain self._add_default_policy_chain_v4v6(ipt_mgr) # create chain based on configured policy self._setup_chains(firewall, ipt_if_prefix, router_fw_ports) # apply the changes immediately (no defer in firewall path) ipt_mgr.defer_apply_off() def _get_chain_name(self, fwid, ver, direction): return '%s%s%s' % (CHAIN_NAME_PREFIX[direction], IP_VER_TAG[ver], fwid) def _setup_chains(self, firewall, ipt_if_prefix, router_fw_ports): """Create Fwaas chain using the rules in the policy """ egress_rule_list = firewall['egress_rule_list'] ingress_rule_list = firewall['ingress_rule_list'] fwid = firewall['id'] ipt_mgr = ipt_if_prefix['ipt'] # default rules for invalid packets and established sessions invalid_rule = self._drop_invalid_packets_rule() est_rule = self._allow_established_rule() for ver in [IPV4, IPV6]: if ver == IPV4: table = ipt_mgr.ipv4['filter'] else: table = ipt_mgr.ipv6['filter'] ichain_name = self._get_chain_name( fwid, ver, constants.INGRESS_DIRECTION) ochain_name = self._get_chain_name( fwid, ver, constants.EGRESS_DIRECTION) for name in [ichain_name, ochain_name]: table.add_chain(name) table.add_rule(name, invalid_rule) table.add_rule(name, est_rule) for rule in ingress_rule_list: if not rule['enabled']: continue iptbl_rule = self._convert_fwaas_to_iptables_rule(rule) if rule['ip_version'] == constants.IP_VERSION_4: ver = IPV4 table = ipt_mgr.ipv4['filter'] else: ver = IPV6 table = ipt_mgr.ipv6['filter'] ichain_name = self._get_chain_name( fwid, ver, constants.INGRESS_DIRECTION) table.add_rule(ichain_name, iptbl_rule) for rule in egress_rule_list: if not rule['enabled']: continue iptbl_rule = self._convert_fwaas_to_iptables_rule(rule) if rule['ip_version'] == constants.IP_VERSION_4: ver = IPV4 table = ipt_mgr.ipv4['filter'] else: ver = IPV6 table = ipt_mgr.ipv6['filter'] ochain_name = self._get_chain_name( fwid, ver, constants.EGRESS_DIRECTION) table.add_rule(ochain_name, iptbl_rule) self._enable_policy_chain(fwid, ipt_if_prefix, router_fw_ports) def _find_changed_rules(self, pre_firewall, firewall): """Find the rules changed between the current firewall and the updating rule """ changed_rules = [] for fw_rule_list in ['egress_rule_list', 'ingress_rule_list']: pre_fw_rules = pre_firewall[fw_rule_list] fw_rules = firewall[fw_rule_list] for pre_fw_rule in pre_fw_rules: for fw_rule in fw_rules: if (pre_fw_rule.get('id') == fw_rule.get('id') and pre_fw_rule != fw_rule): changed_rules.append(pre_fw_rule) changed_rules.append(fw_rule) return changed_rules def _find_removed_rules(self, pre_firewall, firewall): removed_rules = [] for fw_rule_list in ['egress_rule_list', 'ingress_rule_list']: pre_fw_rules = pre_firewall[fw_rule_list] fw_rules = firewall[fw_rule_list] fw_rule_ids = [fw_rule['id'] for fw_rule in fw_rules] removed_rules.extend([pre_fw_rule for pre_fw_rule in pre_fw_rules if pre_fw_rule['id'] not in fw_rule_ids]) return removed_rules def _find_new_rules(self, pre_firewall, firewall): return self._find_removed_rules(firewall, pre_firewall) def _remove_conntrack_new_firewall(self, agent_mode, apply_list, firewall): """Remove conntrack when create new firewall""" routers_list = list(set([apply_info[0] for apply_info in apply_list])) for ri in routers_list: ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix( agent_mode, ri) for ipt_if_prefix in ipt_if_prefix_list: ipt_mgr = ipt_if_prefix['ipt'] self.conntrack.flush_entries(ipt_mgr.namespace) def _remove_conntrack_updated_firewall(self, agent_mode, apply_list, pre_firewall, firewall): """Remove conntrack when updated firewall""" routers_list = list(set([apply_info[0] for apply_info in apply_list])) for ri in routers_list: ipt_if_prefix_list = self._get_ipt_mgrs_with_if_prefix( agent_mode, ri) for ipt_if_prefix in ipt_if_prefix_list: ipt_mgr = ipt_if_prefix['ipt'] ch_rules = self._find_changed_rules(pre_firewall, firewall) i_rules = self._find_new_rules(pre_firewall, firewall) r_rules = self._find_removed_rules(pre_firewall, firewall) removed_conntrack_rules_list = ch_rules + i_rules + r_rules self.conntrack.delete_entries(removed_conntrack_rules_list, ipt_mgr.namespace) def _remove_default_chains(self, nsid): """Remove fwaas default policy chain.""" self._remove_chain_by_name(IPV4, FWAAS_DEFAULT_CHAIN, nsid) self._remove_chain_by_name(IPV6, FWAAS_DEFAULT_CHAIN, nsid) def _remove_chains(self, fwid, ipt_mgr): """Remove fwaas policy chain.""" for ver in [IPV4, IPV6]: for direction in [constants.INGRESS_DIRECTION, constants.EGRESS_DIRECTION]: chain_name = self._get_chain_name(fwid, ver, direction) self._remove_chain_by_name(ver, chain_name, ipt_mgr) def _add_default_policy_chain_v4v6(self, ipt_mgr): dropped_chain = self._get_action_chain(DROPPED_CHAIN) ipt_mgr.ipv4['filter'].add_chain(FWAAS_DEFAULT_CHAIN) ipt_mgr.ipv4['filter'].add_rule( FWAAS_DEFAULT_CHAIN, '-j %s' % dropped_chain) ipt_mgr.ipv6['filter'].add_chain(FWAAS_DEFAULT_CHAIN) ipt_mgr.ipv6['filter'].add_rule( FWAAS_DEFAULT_CHAIN, '-j %s' % dropped_chain) def _add_accepted_chain_v4v6(self, ipt_mgr): v4rules_in_chain = \ ipt_mgr.get_chain("filter", ACCEPTED_CHAIN, ip_version=constants.IP_VERSION_4) if not v4rules_in_chain: ipt_mgr.ipv4['filter'].add_chain(ACCEPTED_CHAIN) ipt_mgr.ipv4['filter'].add_rule(ACCEPTED_CHAIN, '-j ACCEPT') v6rules_in_chain = \ ipt_mgr.get_chain("filter", ACCEPTED_CHAIN, ip_version=constants.IP_VERSION_6) if not v6rules_in_chain: ipt_mgr.ipv6['filter'].add_chain(ACCEPTED_CHAIN) ipt_mgr.ipv6['filter'].add_rule(ACCEPTED_CHAIN, '-j ACCEPT') def _add_dropped_chain_v4v6(self, ipt_mgr): v4rules_in_chain = \ ipt_mgr.get_chain("filter", DROPPED_CHAIN, ip_version=constants.IP_VERSION_4) if not v4rules_in_chain: ipt_mgr.ipv4['filter'].add_chain(DROPPED_CHAIN) ipt_mgr.ipv4['filter'].add_rule(DROPPED_CHAIN, '-j DROP') v6rules_in_chain = \ ipt_mgr.get_chain("filter", DROPPED_CHAIN, ip_version=constants.IP_VERSION_6) if not v6rules_in_chain: ipt_mgr.ipv6['filter'].add_chain(DROPPED_CHAIN) ipt_mgr.ipv6['filter'].add_rule(DROPPED_CHAIN, '-j DROP') def _add_rejected_chain_v4v6(self, ipt_mgr): v4rules_in_chain = \ ipt_mgr.get_chain("filter", REJECTED_CHAIN, ip_version=constants.IP_VERSION_4) if not v4rules_in_chain: ipt_mgr.ipv4['filter'].add_chain(REJECTED_CHAIN) ipt_mgr.ipv4['filter'].add_rule( REJECTED_CHAIN, '-j REJECT --reject-with icmp-port-unreachable') v6rules_in_chain = \ ipt_mgr.get_chain("filter", REJECTED_CHAIN, ip_version=constants.IP_VERSION_6) if not v6rules_in_chain: ipt_mgr.ipv6['filter'].add_chain(REJECTED_CHAIN) ipt_mgr.ipv6['filter'].add_rule( REJECTED_CHAIN, '-j REJECT --reject-with icmp6-port-unreachable') def _remove_chain_by_name(self, ver, chain_name, ipt_mgr): if ver == IPV4: ipt_mgr.ipv4['filter'].remove_chain(chain_name) else: ipt_mgr.ipv6['filter'].remove_chain(chain_name) def _remove_chain_by_name_v4v6(self, chain_name, ipt_mgr): ipt_mgr.ipv4['filter'].remove_chain(chain_name) ipt_mgr.ipv6['filter'].remove_chain(chain_name) def _add_rules_to_chain(self, ipt_mgr, ver, chain_name, rules): if ver == IPV4: table = ipt_mgr.ipv4['filter'] else: table = ipt_mgr.ipv6['filter'] for rule in rules: table.add_rule(chain_name, rule) def _get_action_chain(self, name): binary_name = iptables_manager.binary_name chain_name = iptables_manager.get_chain_name(name) return '%s-%s' % (binary_name, chain_name) def _enable_policy_chain(self, fwid, ipt_if_prefix, router_fw_ports): bname = iptables_manager.binary_name ipt_mgr = ipt_if_prefix['ipt'] if_prefix = ipt_if_prefix['if_prefix'] for (ver, tbl) in [(IPV4, ipt_mgr.ipv4['filter']), (IPV6, ipt_mgr.ipv6['filter'])]: for direction in [constants.INGRESS_DIRECTION, constants.EGRESS_DIRECTION]: chain_name = self._get_chain_name(fwid, ver, direction) chain_name = iptables_manager.get_chain_name(chain_name) if chain_name in tbl.chains: for router_fw_port in router_fw_ports: intf_name = self._get_intf_name(if_prefix, router_fw_port) jump_rule = ['%s %s -j %s-%s' % ( IPTABLES_DIR[direction], intf_name, bname, chain_name)] self._add_rules_to_chain(ipt_mgr, ver, 'FORWARD', jump_rule) # jump to DROP_ALL policy chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN) for router_fw_port in router_fw_ports: intf_name = self._get_intf_name(if_prefix, router_fw_port) jump_rule = ['-o %s -j %s-%s' % (intf_name, bname, chain_name)] self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule) self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule) # jump to DROP_ALL policy chain_name = iptables_manager.get_chain_name(FWAAS_DEFAULT_CHAIN) for router_fw_port in router_fw_ports: intf_name = self._get_intf_name(if_prefix, router_fw_port) jump_rule = ['-i %s -j %s-%s' % (intf_name, bname, chain_name)] self._add_rules_to_chain(ipt_mgr, IPV4, 'FORWARD', jump_rule) self._add_rules_to_chain(ipt_mgr, IPV6, 'FORWARD', jump_rule) def _convert_fwaas_to_iptables_rule(self, rule): action = FWAAS_TO_IPTABLE_ACTION_MAP[rule.get('action')] # Output ordering is important here as it must exactly match what # is returned by iptables-save. If not we risk unnecessarily removing # and readding rules. args = [] args += self._protocol_arg(rule.get('protocol'), rule.get('ip_version')) args += self._ip_prefix_arg('s', rule.get('source_ip_address')) args += self._ip_prefix_arg('d', rule.get('destination_ip_address')) # iptables adds '-m protocol' when any source # or destination port number is specified if (rule.get('source_port') is not None or rule.get('destination_port') is not None): args += self._match_arg(rule.get('protocol')) args += self._port_arg('sport', rule.get('protocol'), rule.get('source_port')) args += self._port_arg('dport', rule.get('protocol'), rule.get('destination_port')) args += self._action_arg(action) iptables_rule = ' '.join(args) return iptables_rule def _drop_invalid_packets_rule(self): dropped_chain = self._get_action_chain(DROPPED_CHAIN) return '-m state --state INVALID -j %s' % dropped_chain def _allow_established_rule(self): return '-m state --state RELATED,ESTABLISHED -j ACCEPT' def _action_arg(self, action): if not action: return [] args = ['-j', self._get_action_chain(action)] return args def _protocol_arg(self, protocol, ip_version): if not protocol: return [] if (protocol == constants.PROTO_NAME_ICMP and ip_version == constants.IP_VERSION_6): protocol = constants.PROTO_NAME_IPV6_ICMP args = ['-p', protocol] return args def _match_arg(self, protocol): if not protocol: return [] protocol_modules = {constants.PROTO_NAME_UDP: 'udp', constants.PROTO_NAME_TCP: 'tcp', constants.PROTO_NAME_ICMP: 'icmp', constants.PROTO_NAME_IPV6_ICMP: 'icmp6'} # iptables adds '-m protocol' when the port number is specified args = ['-m', protocol_modules[protocol]] return args def _port_arg(self, direction, protocol, port): if protocol not in [constants.PROTO_NAME_UDP, constants.PROTO_NAME_TCP] or port is None: return [] args = ['--%s' % direction, '%s' % port] return args def _ip_prefix_arg(self, direction, ip_prefix): if not ip_prefix: return [] args = ['-%s' % direction, '%s' % utils.ip_to_cidr(ip_prefix)] return args ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3053508 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/0000775000175000017500000000000000000000000032521 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/__init_0000664000175000017500000000000000000000000034032 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000117 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/driver_base.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/driver_0000664000175000017500000000341500000000000034101 0ustar00zuulzuul00000000000000# Copyright (C) 2017 Fujitsu 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. import abc import contextlib class FirewallL2DriverBase(object, metaclass=abc.ABCMeta): """Abstract firewall L2 driver base""" def __init__(self, integration_bridge, sg_enabled=False): pass def filter_defer_apply_on(self): """Defer application of filtering rule.""" pass def filter_defer_apply_off(self): """Turn off deferral of rules and apply the rules now.""" pass @property def ports(self): """Returns filtered ports.""" pass @contextlib.contextmanager def defer_apply(self): """Defer apply context.""" self.filter_defer_apply_on() try: yield finally: self.filter_defer_apply_off() def create_firewall_group(self, ports, firewall_group): """Called when a firewall group is created. """ raise NotImplementedError() def update_firewall_group(self, ports, firewall_group): """Called when a firewall group is updated. """ raise NotImplementedError() def delete_firewall_group(self, ports, firewall_group): """Called when a firewall group is deleted. """ raise NotImplementedError() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3053508 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/0000775000175000017500000000000000000000000033474 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000119 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__0000664000175000017500000000000000000000000033762 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022000000000000011447 xustar0000000000000000122 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/noop_driver.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/no0000664000175000017500000000242400000000000034035 0ustar00zuulzuul00000000000000# Copyright (C) 2017 Fujitsu 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 oslo_log import helpers as log_helpers from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2\ import driver_base class NoopFirewallL2Driver(driver_base.FirewallL2DriverBase): @log_helpers.log_method_call def create_firewall_group(self, ports, firewall_group): pass @log_helpers.log_method_call def update_firewall_group(self, ports, firewall_group): pass @log_helpers.log_method_call def delete_firewall_group(self, ports, firewall_group): pass @log_helpers.log_method_call def process_trusted_ports(self, ports): pass @log_helpers.log_method_call def remove_trusted_ports(self, port_ids): pass ././@PaxHeader0000000000000000000000000000022700000000000011456 xustar0000000000000000124 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/ 27 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvsw0000775000175000017500000000000000000000000034143 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023500000000000011455 xustar0000000000000000135 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvsw0000664000175000017500000000141700000000000034150 0ustar00zuulzuul00000000000000# Copyright 2015 # 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_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import firewall OVSFirewallDriver = firewall.OVSFirewallDriver ././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/constants.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvsw0000664000175000017500000000350600000000000034151 0ustar00zuulzuul00000000000000# Copyright 2015 # 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 constants OF_STATE_NOT_TRACKED = "-trk" OF_STATE_TRACKED = "+trk" OF_STATE_NEW_NOT_ESTABLISHED = "+new-est" OF_STATE_NOT_ESTABLISHED = "-est" OF_STATE_ESTABLISHED = "+est" OF_STATE_ESTABLISHED_NOT_REPLY = "+est-rel-rpl" OF_STATE_ESTABLISHED_REPLY = "+est-rel+rpl" OF_STATE_RELATED = "-new-est+rel-inv" OF_STATE_INVALID = "+trk+inv" OF_STATE_NEW = "+new" OF_STATE_NOT_REPLY_NOT_NEW = "-new-rpl" CT_MARK_NORMAL = '0x0' CT_MARK_INVALID = '0x1' REG_PORT = 5 REG_NET = 6 FW_BASE_EGRESS_TABLE = 64 FW_RULES_EGRESS_TABLE = 65 FW_ACCEPT_OR_INGRESS_TABLE = 66 FW_BASE_INGRESS_TABLE = 68 FW_RULES_INGRESS_TABLE = 69 OVS_FIREWALL_TABLES = ( FW_BASE_EGRESS_TABLE, FW_RULES_EGRESS_TABLE, FW_ACCEPT_OR_INGRESS_TABLE, FW_BASE_INGRESS_TABLE, FW_RULES_INGRESS_TABLE, ) PROTOCOLS_WITH_PORTS = (constants.PROTO_NAME_SCTP, constants.PROTO_NAME_TCP, constants.PROTO_NAME_UDP) # Only map protocols that need special handling REVERSE_IP_PROTOCOL_MAP_WITH_PORTS = { constants.IP_PROTOCOL_MAP[proto]: proto for proto in PROTOCOLS_WITH_PORTS} ethertype_to_dl_type_map = { constants.IPv4: constants.ETHERTYPE_IP, constants.IPv6: constants.ETHERTYPE_IPV6, } ././@PaxHeader0000000000000000000000000000023700000000000011457 xustar0000000000000000137 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/exceptions.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvsw0000664000175000017500000000166000000000000034150 0ustar00zuulzuul00000000000000# Copyright 2016, Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron_lib import exceptions from neutron_fwaas._i18n import _ class OVSFWaaSPortNotFound(exceptions.NeutronException): message = _("Port %(port_id)s is not managed by this agent.") class OVSFWaaSTagNotFound(exceptions.NeutronException): message = _("Cannot get vlan tag for port %(port_id)s.") ././@PaxHeader0000000000000000000000000000023500000000000011455 xustar0000000000000000135 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/firewall.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvsw0000664000175000017500000012030100000000000034142 0ustar00zuulzuul00000000000000# Copyright 2015 # 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 netaddr from neutron_lib import constants as lib_const from neutron_lib.plugins.ml2 import ovs_constants as ovs_consts from oslo_log import log as logging from oslo_utils import netutils from neutron.agent import firewall from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2\ import driver_base from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import constants as fwaas_ovs_consts from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import exceptions from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import rules LOG = logging.getLogger(__name__) ACTION_ALLOW = 'allow' # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver. def _replace_register(flow_params, register_number, register_value): """Replace value from flows to given register number 'register_value' key in dictionary will be replaced by register number given by 'register_number' :param flow_params: Dictionary containing defined flows :param register_number: The number of register where value will be stored :param register_value: Key to be replaced by register number """ try: reg_port = flow_params[register_value] del flow_params[register_value] flow_params['reg{:d}'.format(register_number)] = reg_port except KeyError: pass # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # differs only in constants REG_PORT/REG_NET. def create_reg_numbers(flow_params): """Replace reg_(port|net) values with defined register numbers""" _replace_register(flow_params, fwaas_ovs_consts.REG_PORT, 'reg_port') _replace_register(flow_params, fwaas_ovs_consts.REG_NET, 'reg_net') class FirewallGroup(object): def __init__(self, id_): self.id = id_ self.ingress_rules = [] self.egress_rules = [] self.members = {} self.ports = set() def update_rules(self, ingress_rules, egress_rules): """Update firewall group with ingress/egress rules. If a rule has a protocol field, it is normalized to a number here in order to ease later processing. """ def _translate_protocol_to_number(rule): protocol = rule.get('protocol') if protocol is not None: if protocol.isdigit(): rule['protocol'] = int(protocol) elif (rule.get('ethertype') == lib_const.IPv6 and protocol == lib_const.PROTO_NAME_ICMP): rule['protocol'] = lib_const.PROTO_NUM_IPV6_ICMP else: rule['protocol'] = lib_const.IP_PROTOCOL_MAP.get( protocol, protocol) return rule self.ingress_rules = [_translate_protocol_to_number(ir) for ir in ingress_rules] self.egress_rules = [_translate_protocol_to_number(er) for er in egress_rules] def get_ethertype_filtered_addresses(self, ethertype, exclude_addresses=None): exclude_addresses = set(exclude_addresses if exclude_addresses else []) group_addresses = set(self.members.get(ethertype, [])) return list(group_addresses - exclude_addresses) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # differs only in firewall groups list field name class OFPort(object): def __init__(self, port_dict, ovs_port, vlan_tag): self.id = port_dict['device'] self.vlan_tag = vlan_tag self.mac = ovs_port.vif_mac self.lla_address = str(netutils.get_ipv6_addr_by_EUI64( lib_const.IPv6_LLA_PREFIX, self.mac)) self.ofport = ovs_port.ofport self.fw_group = None self.fixed_ips = port_dict.get('fixed_ips', []) self.neutron_port_dict = port_dict.copy() self.allowed_pairs_v4 = self._get_allowed_pairs(port_dict, version=4) self.allowed_pairs_v6 = self._get_allowed_pairs(port_dict, version=6) @staticmethod def _get_allowed_pairs(port_dict, version): aap_dict = port_dict.get('allowed_address_pairs', set()) return {(aap['mac_address'], aap['ip_address']) for aap in aap_dict if netaddr.IPNetwork(aap['ip_address']).version == version} @property def all_allowed_macs(self): macs = {item[0] for item in self.allowed_pairs_v4.union( self.allowed_pairs_v6)} macs.add(self.mac) return macs @property def ipv4_addresses(self): return [ip_addr for ip_addr in [fixed_ip['ip_address'] for fixed_ip in self.fixed_ips] if netaddr.IPAddress(ip_addr).version == 4] @property def ipv6_addresses(self): return [ip_addr for ip_addr in [fixed_ip['ip_address'] for fixed_ip in self.fixed_ips] if netaddr.IPAddress(ip_addr).version == 6] def update(self, port_dict): self.allowed_pairs_v4 = self._get_allowed_pairs(port_dict, version=4) self.allowed_pairs_v6 = self._get_allowed_pairs(port_dict, version=6) # Neighbour discovery uses LLA self.allowed_pairs_v6.add((self.mac, self.lla_address)) self.fixed_ips = port_dict.get('fixed_ips', []) self.neutron_port_dict = port_dict.copy() # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # differs in methods name [s/sg/fwg] and update_rules method. class FWGPortMap(object): def __init__(self): self.ports = {} self.fw_groups = {} # Maps port_id to ofport number self.unfiltered = {} def get_fwg(self, fwg_id): return self.fw_groups.get(fwg_id, None) def get_or_create_fwg(self, fwg_id): fw_group = self.get_fwg(fwg_id) if not fw_group: fw_group = FirewallGroup(fwg_id) self.fw_groups[fwg_id] = fw_group return fw_group def delete_fwg(self, fwg_id): del self.fw_groups[fwg_id] # XXX NOTE(ivasilevskaya) couldn't find any logical definition why # firewall_group should come as 3rd argument instead of adding fwg_id # to port_dict. Removed in favor of SG api def create_port(self, port, port_dict): self.ports[port.id] = port self.update_port(port, port_dict) # XXX NOTE(ivasilevskaya) couldn't find any logical definition why # firewall_group should come as 3rd argument instead of adding fwg_id # to port_dict. Removed in favor of SG api def update_port(self, port, port_dict): for fw_group in self.fw_groups.values(): fw_group.ports.discard(port) fw_group = self.get_or_create_fwg(port_dict['firewall_group']) port.fw_group = fw_group fw_group.ports.add(port) port.update(port_dict) def remove_port(self, port): if port.fw_group: port.fw_group.ports.discard(port) del self.ports[port.id] def update_rules(self, fwg_id, ingress_rules, egress_rules): fw_group = self.get_or_create_fwg(fwg_id) fw_group.update_rules(ingress_rules, egress_rules) def update_members(self, fwg_id, members): fw_group = self.get_or_create_fwg(fwg_id) fw_group.members = members # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver that # doesn't have a conjunction manager because no remote_group_id concept is # applicable to firewall groups class OVSFirewallDriver(driver_base.FirewallL2DriverBase): REQUIRED_PROTOCOLS = [ ovs_consts.OPENFLOW10, ovs_consts.OPENFLOW11, ovs_consts.OPENFLOW12, ovs_consts.OPENFLOW13, ovs_consts.OPENFLOW14, ] provides_arp_spoofing_protection = True # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver. # This driver won't have any conj_manager logic because there is no concept # of remote_group_id for firewall groups (that I know of at least) def __init__(self, agent_api, sg_with_ovs=False): """Initialize object""" integration_bridge = agent_api.request_int_br() self.int_br = self.initialize_bridge(integration_bridge) self.fwg_port_map = FWGPortMap() self.fwg_to_delete = set() self._deferred = False self.sg_with_ovs = sg_with_ovs self._drop_all_unmatched_flows() self._initialize_third_party_tables() # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _accept_flow(self, **flow): for f in rules.create_accept_flows(flow, self.sg_with_ovs): self._add_flow(**f) def _drop_flow(self, **flow): for f in rules.create_drop_flows(flow): self._add_flow(**f) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _add_flow(self, **kwargs): dl_type = kwargs.get('dl_type') create_reg_numbers(kwargs) if isinstance(dl_type, int): kwargs['dl_type'] = "0x{:04x}".format(dl_type) if self._deferred: self.int_br.add_flow(**kwargs) else: self.int_br.br.add_flow(**kwargs) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _delete_flows(self, **kwargs): create_reg_numbers(kwargs) if self._deferred: self.int_br.delete_flows(**kwargs) else: self.int_br.br.delete_flows(**kwargs) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def _strict_delete_flow(self, **kwargs): """Delete given flow right away even if bridge is deferred. Delete command will use strict delete. """ create_reg_numbers(kwargs) self.int_br.br.delete_flows(strict=True, **kwargs) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver @staticmethod def initialize_bridge(int_br): int_br.add_protocols(*OVSFirewallDriver.REQUIRED_PROTOCOLS) return int_br.deferred(full_ordered=True) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver, # differs in constants def _drop_all_unmatched_flows(self): for table in fwaas_ovs_consts.OVS_FIREWALL_TABLES: if (table == fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE and self.sg_with_ovs): continue self.int_br.br.add_flow(table=table, priority=0, actions='drop') def _initialize_third_party_tables(self): self.int_br.br.add_flow( table=ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE, priority=1, actions='normal') for table in (ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE, ovs_consts.DROPPED_TRAFFIC_TABLE): self.int_br.br.add_flow( table=table, priority=0, actions='drop') # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def get_ovs_port(self, port_id): ovs_port = self.int_br.br.get_vif_port_by_id(port_id) if not ovs_port: raise exceptions.OVSFWaaSPortNotFound(port_id=port_id) return ovs_port def _get_port_vlan_tag(self, port): vlan_tag = port.get('lvlan', None) if not vlan_tag: raise exceptions.OVSFWaaSTagNotFound(port_id=port['device']) return vlan_tag # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def get_ofport(self, port): port_id = port['device'] return self.fwg_port_map.ports.get(port_id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver, # self.sg_port_map -> self.fwg_port_map def get_or_create_ofport(self, port): """Get ofport specified by port['device'], checking and reflecting ofport changes. If ofport is nonexistent, create and return one. """ port_id = port['device'] ovs_port = self.get_ovs_port(port_id) try: of_port = self.fwg_port_map.ports[port_id] except KeyError: port_vlan_id = self._get_port_vlan_tag(port) of_port = OFPort(port, ovs_port, port_vlan_id) self.fwg_port_map.create_port(of_port, port) else: if of_port.ofport != ovs_port.ofport: self.fwg_port_map.remove_port(of_port) of_port = OFPort(port, ovs_port, of_port.vlan_tag) self.fwg_port_map.update_port(of_port, port) return of_port # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def is_port_managed(self, port): return port['device'] in self.fwg_port_map.ports # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def prepare_port_filter(self, port): # NOTE(annp): port no security should be handled by security group in # co-existence mode, otherwise(standalone mode) fwg will handle it. if not firewall.port_sec_enabled(port) and not self.sg_with_ovs: self._initialize_egress_no_port_security(port) return old_of_port = self.get_ofport(port) # Make sure delete old allow_address_pair MACs because # allow_address_pair MACs will be updated in # self.get_or_create_ofport(port) if old_of_port: LOG.error("Initializing port %s that was already " "initialized.", port['device']) self.delete_all_port_flows(old_of_port) of_port = self.get_or_create_ofport(port) self.initialize_port_flows(of_port) self.add_flows_from_rules(of_port) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def update_port_filter(self, port): """Update rules for given port Current existing filtering rules are removed and new ones are generated based on current loaded firewall group rules and members. Note: port no security should be handled by security group in co-existence mode, otherwise fwg will handle it. """ if not firewall.port_sec_enabled(port) and not self.sg_with_ovs: self.remove_port_filter(port) self._initialize_egress_no_port_security(port) return elif not self.is_port_managed(port): if not self.sg_with_ovs: self._remove_egress_no_port_security(port['device']) self.prepare_port_filter(port) return old_of_port = self.get_ofport(port) of_port = self.get_or_create_ofport(port) # TODO(jlibosva): Handle firewall blink self.delete_all_port_flows(old_of_port) self.initialize_port_flows(of_port) self.add_flows_from_rules(of_port) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver, # sg_port_map -> fwg_port_map def remove_port_filter(self, port): """Remove port from firewall All flows related to this port are removed from ovs. Port is also removed from ports managed by this firewall. """ if self.is_port_managed(port): of_port = self.get_ofport(port) self.delete_all_port_flows(of_port) self.fwg_port_map.remove_port(of_port) self._schedule_fwg_deletion_maybe(of_port.fw_group.id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with ingress\egress rules arguments instead of single rules def update_firewall_group_rules(self, fwg_id, ingress_rules, egress_rules): self.fwg_port_map.update_rules(fwg_id, ingress_rules, egress_rules) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with sg_port_map -> fwg_port_map def _schedule_fwg_deletion_maybe(self, fwg_id): """Schedule possible deletion of the given firewall group. This function must be called when the number of ports associated to fwg_id drops to zero, as it isn't possible to know FWG deletions from agents due to RPC API design. """ fwg_group = self.fwg_port_map.get_or_create_fwg(fwg_id) if not fwg_group.members or not fwg_group.ports: self.fwg_to_delete.add(fwg_id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with sg_port_map -> fwg_port_map def _cleanup_stale_fwg(self): fwg_to_delete = self.fwg_to_delete self.fwg_to_delete = set() for fwg_id in fwg_to_delete: fw_group = self.fwg_port_map.get_fwg(fwg_id) if fw_group.members and fw_group.ports: # firewall group is still in use continue self.fwg_port_map.delete_fwg(fwg_id) def process_trusted_ports(self, ports): """Pass packets from these ports directly to ingress pipeline.""" if self.sg_with_ovs: return for port in ports: self._initialize_egress_no_port_security(port) def remove_trusted_ports(self, port_ids): if self.sg_with_ovs: return for port_id in port_ids: self._remove_egress_no_port_security(port_id) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def filter_defer_apply_on(self): self._deferred = True # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver def filter_defer_apply_off(self): if self._deferred: self._cleanup_stale_fwg() self.int_br.apply_flows() self._deferred = False # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # with sg_port_map -> fwg_port_map @property def ports(self): return {id_: port.neutron_port_dict for id_, port in self.fwg_port_map.ports.items()} # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def initialize_port_flows(self, port): """Set base flows for port :param port: OFPort instance """ # Identify egress flow self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=105, in_port=port.ofport, actions='set_field:{:d}->reg{:d},' 'set_field:{:d}->reg{:d},' 'resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, port.vlan_tag, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_BASE_EGRESS_TABLE) ) # Identify ingress flows after egress filtering for mac_addr in port.all_allowed_macs: self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=95, dl_dst=mac_addr, dl_vlan='0x%x' % port.vlan_tag, actions='set_field:{:d}->reg{:d},' 'set_field:{:d}->reg{:d},' 'strip_vlan,resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, port.vlan_tag, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), ) self._initialize_egress(port) self._initialize_ingress(port) def _fwaas_process_colocated_ingress(self, port): for mac_addr in port.all_allowed_macs: self._add_flow( table=ovs_consts.ACCEPT_OR_INGRESS_TABLE, priority=105, dl_dst=mac_addr, reg_net=port.vlan_tag, actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_egress_ipv6_icmp(self, port): for icmp_type in firewall.ICMPV6_ALLOWED_EGRESS_TYPES: self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=95, in_port=port.ofport, reg_port=port.ofport, dl_type=lib_const.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=icmp_type, actions='normal') # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) and exception classes def _initialize_egress_no_port_security(self, port): port_id = port['device'] try: ovs_port = self.get_ovs_port(port_id) vlan_tag = self._get_port_vlan_tag(port) except exceptions.OVSFWaaSTagNotFound: # It's a patch port, don't set anything return except exceptions.OVSFWaaSPortNotFound as not_found_e: LOG.error("Initializing unfiltered port %(port_id)s that does not " "exist in ovsdb: %(err)s.", {'port_id': port_id, 'err': not_found_e}) return self.fwg_port_map.unfiltered[port_id] = ovs_port.ofport self._add_flow( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=ovs_port.ofport, actions='set_field:%d->reg%d,' 'set_field:%d->reg%d,' 'resubmit(,%d)' % ( ovs_port.ofport, fwaas_ovs_consts.REG_PORT, vlan_tag, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE) ) self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=80, reg_port=ovs_port.ofport, actions='normal', ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _remove_egress_no_port_security(self, port_id): try: ofport = self.fwg_port_map.unfiltered[port_id] except KeyError: LOG.debug("Port %s is not handled by the firewall.", port_id) return self._delete_flows( table=ovs_consts.TRANSIENT_TABLE, in_port=ofport ) self._delete_flows( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, reg_port=ofport ) del self.fwg_port_map.unfiltered[port_id] # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_egress(self, port): """Identify egress traffic and send it to egress base""" self._initialize_egress_ipv6_icmp(port) # Apply mac/ip pairs for IPv4 allowed_pairs = port.allowed_pairs_v4.union( {(port.mac, ip_addr) for ip_addr in port.ipv4_addresses}) for mac_addr, ip_addr in allowed_pairs: self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=95, in_port=port.ofport, reg_port=port.ofport, dl_src=mac_addr, dl_type=lib_const.ETHERTYPE_ARP, arp_spa=ip_addr, actions='normal' ) self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=65, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, dl_type=lib_const.ETHERTYPE_IP, in_port=port.ofport, dl_src=mac_addr, nw_src=ip_addr, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, fwaas_ovs_consts.REG_NET) ) # Apply mac/ip pairs for IPv6 allowed_pairs = port.allowed_pairs_v6.union( {(port.mac, ip_addr) for ip_addr in port.ipv6_addresses}) for mac_addr, ip_addr in allowed_pairs: self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=65, reg_port=port.ofport, in_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, dl_type=lib_const.ETHERTYPE_IPV6, dl_src=mac_addr, ipv6_src=ip_addr, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, fwaas_ovs_consts.REG_NET) ) # DHCP discovery accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE if self.sg_with_ovs: accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE for dl_type, src_port, dst_port in ( (lib_const.ETHERTYPE_IP, 68, 67), (lib_const.ETHERTYPE_IPV6, 546, 547)): self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=80, reg_port=port.ofport, in_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='resubmit(,{:d})'.format(accept_or_ingress) ) # Ban dhcp service running on an instance for dl_type, src_port, dst_port in ( (lib_const.ETHERTYPE_IP, 67, 68), (lib_const.ETHERTYPE_IPV6, 547, 546)): self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=70, in_port=port.ofport, reg_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop Router Advertisements from instances self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=70, in_port=port.ofport, reg_port=port.ofport, dl_type=lib_const.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=lib_const.ICMPV6_TYPE_RA, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop all remaining not tracked egress connections self._add_flow( table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE, priority=10, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, in_port=port.ofport, reg_port=port.ofport, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Fill in accept_or_ingress table by checking that traffic is ingress # and if not, accept it if self.sg_with_ovs: self._fwaas_process_colocated_ingress(port) else: for mac_addr in port.all_allowed_macs: self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=100, dl_dst=mac_addr, reg_net=port.vlan_tag, actions='set_field:{:d}->reg{:d},resubmit(,{:d})'.format( port.ofport, fwaas_ovs_consts.REG_PORT, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), ) for ethertype in [lib_const.ETHERTYPE_IP, lib_const.ETHERTYPE_IPV6]: self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=90, dl_type=ethertype, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, actions='ct(commit,zone=NXM_NX_REG{:d}[0..15]),' 'resubmit(,{:d})'.format( fwaas_ovs_consts.REG_NET, ovs_consts.ACCEPTED_EGRESS_TRAFFIC_TABLE) ) self._add_flow( table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE, priority=80, reg_port=port.ofport, actions='normal' ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_tracked_egress(self, port): # Drop invalid packets self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=50, ct_state=fwaas_ovs_consts.OF_STATE_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop traffic for removed fwg rules self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=50, reg_port=port.ofport, ct_mark=fwaas_ovs_consts.CT_MARK_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for state in ( fwaas_ovs_consts.OF_STATE_ESTABLISHED_REPLY, fwaas_ovs_consts.OF_STATE_RELATED, ): self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=50, ct_state=state, ct_mark=fwaas_ovs_consts.CT_MARK_NORMAL, reg_port=port.ofport, ct_zone=port.vlan_tag, actions='normal' ) self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=40, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_ESTABLISHED, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for ethertype in [lib_const.ETHERTYPE_IP, lib_const.ETHERTYPE_IPV6]: self._add_flow( table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, priority=40, dl_type=ethertype, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED, actions="ct(commit,zone=NXM_NX_REG{:d}[0..15]," "exec(set_field:{:s}->ct_mark))".format( fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.CT_MARK_INVALID) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_ingress_ipv6_icmp(self, port): for icmp_type in firewall.ICMPV6_ALLOWED_INGRESS_TYPES: self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=100, reg_port=port.ofport, dl_dst=port.mac, dl_type=lib_const.ETHERTYPE_IPV6, nw_proto=lib_const.PROTO_NUM_IPV6_ICMP, icmp_type=icmp_type, actions='output:{:d}'.format(port.ofport) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_ingress(self, port): # Allow incoming ARPs self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=100, dl_type=lib_const.ETHERTYPE_ARP, reg_port=port.ofport, actions='output:{:d}'.format(port.ofport) ) self._initialize_ingress_ipv6_icmp(port) # DHCP offers for dl_type, src_port, dst_port in ( (lib_const.ETHERTYPE_IP, 67, 68), (lib_const.ETHERTYPE_IPV6, 547, 546)): self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=95, reg_port=port.ofport, dl_type=dl_type, nw_proto=lib_const.PROTO_NUM_UDP, tp_src=src_port, tp_dst=dst_port, actions='output:{:d}'.format(port.ofport) ) # Track untracked for dl_type in (lib_const.ETHERTYPE_IP, lib_const.ETHERTYPE_IPV6): self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, priority=90, reg_port=port.ofport, dl_type=dl_type, ct_state=fwaas_ovs_consts.OF_STATE_NOT_TRACKED, actions='ct(table={:d},zone=NXM_NX_REG{:d}[0..15])'.format( fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, fwaas_ovs_consts.REG_NET) ) self._add_flow( table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE, ct_state=fwaas_ovs_consts.OF_STATE_TRACKED, priority=80, reg_port=port.ofport, actions='resubmit(,{:d})'.format( fwaas_ovs_consts.FW_RULES_INGRESS_TABLE) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def _initialize_tracked_ingress(self, port): # Drop invalid packets self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=50, ct_state=fwaas_ovs_consts.OF_STATE_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Drop traffic for removed fwg rules self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=50, reg_port=port.ofport, ct_mark=fwaas_ovs_consts.CT_MARK_INVALID, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) # Allow established and related connections for state in (fwaas_ovs_consts.OF_STATE_ESTABLISHED_REPLY, fwaas_ovs_consts.OF_STATE_RELATED): self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=50, reg_port=port.ofport, ct_state=state, ct_mark=fwaas_ovs_consts.CT_MARK_NORMAL, ct_zone=port.vlan_tag, actions='output:{:d}'.format(port.ofport) ) self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=40, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_NOT_ESTABLISHED, actions='resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE ) for ethertype in [lib_const.ETHERTYPE_IP, lib_const.ETHERTYPE_IPV6]: self._add_flow( table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, priority=40, dl_type=ethertype, reg_port=port.ofport, ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED, actions="ct(commit,zone=NXM_NX_REG{:d}[0..15]," "exec(set_field:{:s}->ct_mark))".format( fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.CT_MARK_INVALID) ) # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) and rules_generator method def add_flows_from_rules(self, port): self._initialize_tracked_ingress(port) self._initialize_tracked_egress(port) LOG.debug('Creating flow rules for port %s that is port %d in OVS', port.id, port.ofport) for rule in self.create_rules_generator_for_port(port): flows = rules.create_flows_from_rule_and_port(rule, port) LOG.debug("RULGEN: Rules generated for flow %s are %s", rule, flows) for flow in flows: if rule.get('action') == ACTION_ALLOW: self._accept_flow(**flow) else: self._drop_flow(**flow) def create_rules_generator_for_port(self, port): """Returns a generator emitting rules valid for further processing Injects necessary fields to feed one-by-one to rules module to transform into valid openflow rules. """ def inject_fields(rule, direction, offset=0): """Add fields to rule dict to be able to utilize rules module Currently such fields are added: 'offset', 'direction', 'ethertype', 'source_port_range_min', 'source_port_range_max', 'port_range_min', 'port_range_max' """ # XXX NOTE(ivasilevskaya) maybe there's a clever way to do that version_ethertype_map = {lib_const.IP_VERSION_4: lib_const.IPv4, lib_const.IP_VERSION_6: lib_const.IPv6} rule['direction'] = direction rule['ethertype'] = version_ethertype_map[rule['ip_version']] rule['offset'] = offset # transfer destination_port into port_range_min/port_range_max def add_range(range_key, key_min, key_max): range_str = rule.get(range_key) if not range_str: return ports = range_str.split(':', 1) rule[key_min] = int(ports[0]) rule[key_max] = ( int(ports[1]) if len(ports) == 2 else int(ports[0])) add_range('destination_port', 'port_range_min', 'port_range_max') add_range('source_port', 'source_port_range_min', 'source_port_range_max') # add direction field offset = len(port.fw_group.ingress_rules) - 1 for rule in port.fw_group.ingress_rules: inject_fields(rule, lib_const.INGRESS_DIRECTION, offset) offset -= 1 yield rule offset = len(port.fw_group.egress_rules) - 1 for rule in port.fw_group.egress_rules: inject_fields(rule, lib_const.EGRESS_DIRECTION, offset) offset -= 1 yield rule # NOTE(ivasilevskaya) That's a copy-paste from neutron ovsfw driver # which differs in constants (table numbers) def delete_all_port_flows(self, port): """Delete all flows for given port""" accept_or_ingress = fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE if self.sg_with_ovs: accept_or_ingress = ovs_consts.ACCEPT_OR_INGRESS_TABLE for mac_addr in port.all_allowed_macs: self._strict_delete_flow(priority=95, table=ovs_consts.TRANSIENT_TABLE, dl_dst=mac_addr, dl_vlan=port.vlan_tag) self._delete_flows( table=accept_or_ingress, dl_dst=mac_addr, reg_net=port.vlan_tag) self._strict_delete_flow(priority=105, table=ovs_consts.TRANSIENT_TABLE, in_port=port.ofport) self._delete_flows(reg_port=port.ofport) def create_firewall_group(self, ports_for_fwg, firewall_group): egress_rules = firewall_group['egress_rule_list'] ingress_rules = firewall_group['ingress_rule_list'] fwg_id = firewall_group['id'] self.update_firewall_group_rules(fwg_id, ingress_rules, egress_rules) for port in ports_for_fwg: port['firewall_group'] = fwg_id self.update_port_filter(port) def update_firewall_group(self, ports_for_fwg, firewall_group): self.create_firewall_group(ports_for_fwg, firewall_group) def delete_firewall_group(self, ports_for_fwg, firewall_group): for port in ports_for_fwg: port['firewall_group'] = firewall_group['id'] self.remove_port_filter(port) ././@PaxHeader0000000000000000000000000000023200000000000011452 xustar0000000000000000132 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/rules.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvsw0000664000175000017500000001666300000000000034161 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import netaddr from neutron.common import utils from neutron_lib import constants as n_consts from neutron_lib.plugins.ml2 import ovs_constants as ovs_consts from oslo_log import log as logging from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import constants as fwaas_ovs_consts LOG = logging.getLogger(__name__) # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver, differs in # constants CT_STATES = [ fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY, fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED] # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver FLOW_FIELD_FOR_IPVER_AND_DIRECTION = { (n_consts.IP_VERSION_4, n_consts.EGRESS_DIRECTION): 'nw_dst', (n_consts.IP_VERSION_6, n_consts.EGRESS_DIRECTION): 'ipv6_dst', (n_consts.IP_VERSION_4, n_consts.INGRESS_DIRECTION): 'nw_src', (n_consts.IP_VERSION_6, n_consts.INGRESS_DIRECTION): 'ipv6_src', } # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver FORBIDDEN_PREFIXES = (n_consts.IPv4_ANY, n_consts.IPv6_ANY) # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def is_valid_prefix(ip_prefix): # IPv6 have multiple ways how to describe ::/0 network, converting to # IPNetwork and back to string unifies it return (ip_prefix and str(netaddr.IPNetwork(ip_prefix)) not in FORBIDDEN_PREFIXES) # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def create_flows_from_rule_and_port(rule, port): ethertype = rule['ethertype'] direction = rule['direction'] dst_ip_prefix = rule.get('dest_ip_prefix') src_ip_prefix = rule.get('source_ip_prefix') offset = int(rule.get('offset', 0)) flow_template = { 'priority': 70 + offset, 'dl_type': fwaas_ovs_consts.ethertype_to_dl_type_map[ethertype], 'reg_port': port.ofport, } if is_valid_prefix(dst_ip_prefix): flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( utils.get_ip_version(dst_ip_prefix), n_consts.EGRESS_DIRECTION)] ] = dst_ip_prefix if is_valid_prefix(src_ip_prefix): flow_template[FLOW_FIELD_FOR_IPVER_AND_DIRECTION[( utils.get_ip_version(src_ip_prefix), n_consts.INGRESS_DIRECTION)] ] = src_ip_prefix flows = create_protocol_flows(direction, flow_template, port, rule) return flows # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver, differs in # constants def populate_flow_common(direction, flow_template, port): """Initialize common flow fields.""" if direction == n_consts.INGRESS_DIRECTION: flow_template['table'] = fwaas_ovs_consts.FW_RULES_INGRESS_TABLE flow_template['actions'] = "output:{:d}".format(port.ofport) elif direction == n_consts.EGRESS_DIRECTION: flow_template['table'] = fwaas_ovs_consts.FW_RULES_EGRESS_TABLE # Traffic can be both ingress and egress, check that no ingress rules # should be applied flow_template['actions'] = 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE) return flow_template # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def create_protocol_flows(direction, flow_template, port, rule): flow_template = populate_flow_common(direction, flow_template.copy(), port) protocol = rule.get('protocol') if protocol is not None: flow_template['nw_proto'] = protocol if protocol in [n_consts.PROTO_NUM_ICMP, n_consts.PROTO_NUM_IPV6_ICMP]: flows = create_icmp_flows(flow_template, rule) else: flows = create_port_range_flows(flow_template, rule) return flows or [flow_template] # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver, differs only in # constant def create_port_range_flows(flow_template, rule): protocol = fwaas_ovs_consts.REVERSE_IP_PROTOCOL_MAP_WITH_PORTS.get( rule.get('protocol')) if protocol is None: return [] flows = [] src_port_match = '{:s}_src'.format(protocol) src_port_min = rule.get('source_port_range_min') src_port_max = rule.get('source_port_range_max') dst_port_match = '{:s}_dst'.format(protocol) dst_port_min = rule.get('port_range_min') dst_port_max = rule.get('port_range_max') dst_port_range = [] if dst_port_min and dst_port_max: dst_port_range = utils.port_rule_masking(dst_port_min, dst_port_max) src_port_range = [] if src_port_min and src_port_max: src_port_range = utils.port_rule_masking(src_port_min, src_port_max) for port in src_port_range: flow = flow_template.copy() flow[src_port_match] = port if dst_port_range: for port in dst_port_range: dst_flow = flow.copy() dst_flow[dst_port_match] = port flows.append(dst_flow) else: flows.append(flow) else: for port in dst_port_range: flow = flow_template.copy() flow[dst_port_match] = port flows.append(flow) return flows # NOTE(ivasilevskaya) copy-paste from neutron ovsfw driver def create_icmp_flows(flow_template, rule): icmp_type = rule.get('port_range_min') if icmp_type is None: return flow = flow_template.copy() flow['icmp_type'] = icmp_type icmp_code = rule.get('port_range_max') if icmp_code is not None: flow['icmp_code'] = icmp_code return [flow] def resubmit_to_sg(flow): if flow['table'] == fwaas_ovs_consts.FW_RULES_EGRESS_TABLE: flow['actions'] = 'resubmit(,{:d})'.format( ovs_consts.RULES_EGRESS_TABLE) if flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE: flow['actions'] = 'resubmit(,{:d})'.format( ovs_consts.RULES_INGRESS_TABLE) def create_accept_flows(flow, sg_enabled=False): flow['ct_state'] = CT_STATES[0] if sg_enabled: resubmit_to_sg(flow) result = [flow.copy()] flow['ct_state'] = CT_STATES[1] if sg_enabled: resubmit_to_sg(flow) elif flow['table'] == fwaas_ovs_consts.FW_RULES_INGRESS_TABLE: flow['actions'] = ( 'ct(commit,zone=NXM_NX_REG{:d}[0..15]),{:s},' 'resubmit(,{:d})'.format( fwaas_ovs_consts.REG_NET, flow['actions'], ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE) ) result.append(flow) return result def create_drop_flows(flow): if flow['table'] in [fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, fwaas_ovs_consts.FW_RULES_EGRESS_TABLE]: flow['actions'] = 'resubmit(,%d)' % ovs_consts.DROPPED_TRAFFIC_TABLE flow['ct_state'] = fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED result = [flow.copy()] flow['ct_state'] = fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY result.append(flow) return result ././@PaxHeader0000000000000000000000000000021500000000000011453 xustar0000000000000000119 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/legacy_conntrack.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/legacy_con0000664000175000017500000002261200000000000034235 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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.agent.linux import utils as linux_utils from neutron_lib import constants from oslo_log import log as logging from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ conntrack_base LOG = logging.getLogger(__name__) IP_VERSIONS = [constants.IP_VERSION_4, constants.IP_VERSION_6] ATTR_POSITIONS = { 'icmp': (('type', 5), ('code', 6), ('src', 3), ('dst', 4), ('id', 7)), 'icmpv6': (('type', 5), ('code', 6), ('src', 3), ('dst', 4), ('id', 7)), 'tcp': (('sport', 6), ('dport', 7), ('src', 4), ('dst', 5)), 'udp': (('sport', 5), ('dport', 6), ('src', 3), ('dst', 4)) } def normalize_filters_tuple(rule_tuple): new_rule = [] for el in rule_tuple: if el is None: new_rule.append('') else: new_rule.append(el) return tuple(new_rule) class ConntrackLegacy(conntrack_base.ConntrackDriverBase): def initialize(self, execute=None): LOG.debug('Initialize Conntrack Legacy') self.execute = execute or linux_utils.execute def flush_entries(self, namespace): prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else [] cmd = prefixcmd + ['conntrack', '-D'] self._execute_command(cmd) def delete_entries(self, rules, namespace): rule_filters = [self._get_filter_from_rule(r) for r in rules] rule_filters.sort(key=normalize_filters_tuple) delete_entries = self._get_entries_to_delete( rule_filters, self.list_entries(namespace)) for delete_entry in delete_entries: cmd = self._get_conntrack_cmd_from_entry(delete_entry, namespace) self._execute_command(cmd) def _execute_command(self, cmd): try: output = self.execute(cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1], privsep_exec=True) except RuntimeError: msg = "Failed execute conntrack command %s" % cmd raise RuntimeError(msg) return output def list_entries(self, namespace): """List and parse all conntrack entries :param namespace: namespace to get conntrack entries :returns: sorted list of conntrack entries in Python tuple for example: [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 1234), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')] """ parsed_entries = [] prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else [] for ip_version in IP_VERSIONS: cmd = prefixcmd + ['conntrack', '-L', '-f', 'ipv' + str(ip_version)] raw_entries = self._execute_command(cmd).splitlines() for raw_entry in raw_entries: parsed_entry = self._parse_entry(raw_entry.split(), ip_version) if parsed_entry is not None: parsed_entries.append(parsed_entry) return sorted(parsed_entries) def _get_conntrack_cmd_from_entry(self, entry, namespace): prefixcmd = ['ip', 'netns', 'exec', namespace] if namespace else [] cmd = ['conntrack', '-D'] contrack_filter = ['-f', 'ipv' + str(entry[0]), '-p', entry[1]] if entry[1] in ['icmp', 'icmpv6']: contrack_filter.extend(['--icmp-type', entry[2], '--icmp-code', entry[3], '-s', entry[4], '-d', entry[5], '--icmp-id', entry[6]]) else: contrack_filter.extend(['--sport', entry[2], '--dport', entry[3], '-s', entry[4], '-d', entry[5]]) exec_cmd = prefixcmd + cmd + contrack_filter return exec_cmd def _parse_entry(self, entry, ip_version): """Parse entry from text to Python tuple :param entry: conntrack entry as a list of string :param ip_version: ip version 4 or 6 :returns: conntrack entry in Python tuple for example: (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') The attributes are ordered to be easy to compare with other entries and compare with firewall rule """ protocol = entry[0] if protocol not in ATTR_POSITIONS: LOG.warning( 'Skipping conntrack entry %s with unsupported protocol', entry) return None parsed_entry = [ip_version, protocol] for attr, position in ATTR_POSITIONS[protocol]: val = entry[position].partition('=')[2] parsed_entry.append(int(val) if attr in ['sport', 'dport', 'type', 'code', 'id'] else val) return tuple(parsed_entry) def _get_entries_to_delete(self, rule_filters, entries): """Specify conntrack entries to delete :param rule_filters: List of filters parsed from firewall rules :param entries: all entries within namespace :returns: conntrack entries to delete """ # List all entries from namespace, they are already parsed # to a list of tuples: # [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 1234), # (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')] delete_entries = [] entry_index = 0 entry_number = len(entries) for rule_filter in rule_filters: while entry_index < entry_number: # Compare entry with rule comp = self._compare_entry_and_rule_filter( rule_filter, entries[entry_index]) # Increase entry_index when entry is under rule if comp < 0: entry_index += 1 # Append entry to delete_entry if it matches with rule elif comp == 0: delete_entries.append(entries[entry_index]) entry_index += 1 # Switch to new higher rule else: break return delete_entries @staticmethod def _get_filter_from_rule(rule): """Parse the firewall rule to a tuple :param rule: firewall rule :returns: a tuple of parsed information """ rule_filter = [] keys = ('ip_version', 'protocol', 'source_port', 'destination_port', 'source_ip_address', 'destination_ip_address') for key in keys: if key in ('source_port', 'destination_port'): port_range = rule.get(key, []) if port_range: port_lower, sep, port_upper = port_range.partition(':') port_upper = port_upper if sep else port_lower port_range = [port_lower, port_upper] rule_filter.append(port_range or []) else: rule_filter.append(rule.get(key, [])) return tuple(rule_filter) @staticmethod def _compare_entry_and_rule_filter(rule_filter, entry): """Define that the entry should be deleted or not :param rule_filter: filter that is parsed from a firewall rule for example: (4, 'tcp', ['22', '33'], ['44', '55']) :param entry: parsed conntrack entry, for example: (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') :returns: -1 if entry is lower than rule 0 if entry matches rule, 1 if entry is higher than rule """ ENTRY_IS_LOWER = -1 ENTRY_MATCHES = 0 ENTRY_IS_HIGHER = 1 rule_ip_version = rule_filter[0] if entry[0] < rule_ip_version: return ENTRY_IS_LOWER elif entry[0] > rule_ip_version: return ENTRY_IS_HIGHER rule_protocol = rule_filter[1] if rule_protocol == constants.PROTO_NAME_IPV6_ICMP: rule_protocol = constants.PROTO_NAME_IPV6_ICMP_LEGACY if rule_protocol: if entry[1] < rule_protocol: return ENTRY_IS_LOWER elif entry[1] > rule_protocol: return ENTRY_IS_HIGHER sport_range = rule_filter[2] if sport_range: sport_range = [int(port) for port in sport_range] if entry[2] < min(sport_range[0], sport_range[-1]): return ENTRY_IS_LOWER elif entry[2] > max(sport_range[0], sport_range[-1]): return ENTRY_IS_HIGHER dport_range = rule_filter[3] if dport_range: dport_range = [int(port) for port in dport_range] if entry[3] < min(dport_range[0], dport_range[-1]): return ENTRY_IS_LOWER elif entry[3] > max(dport_range[0], dport_range[-1]): return ENTRY_IS_HIGHER return ENTRY_MATCHES ././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/netlink_conntrack.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/netlink_co0000664000175000017500000001277400000000000034267 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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 constants from oslo_log import log as logging from neutron_fwaas.privileged import netlink_lib as nl_lib from neutron_fwaas.services.firewall.service_drivers.agents.drivers import\ conntrack_base LOG = logging.getLogger(__name__) class ConntrackNetlink(conntrack_base.ConntrackDriverBase): def initialize(self, *args, **kwargs): LOG.debug('Conntrack Netlink loaded') def flush_entries(self, namespace): """Flush all conntrack entries within the namespace :param namespace: namespace to flush :return: None """ nl_lib.flush_entries(namespace) def delete_entries(self, rules, namespace): rule_filters = (self._get_filter_from_rule(r) for r in rules) rule_filters = sorted(rule_filters) entries = nl_lib.list_entries(namespace) delete_entries = self._get_entries_to_delete(rule_filters, entries) if delete_entries: nl_lib.delete_entries(delete_entries, namespace) def _get_entries_to_delete(self, rule_filters, entries): """Specify conntrack entries to delete :param rule_filters: List of filters parsed from firewall rules :param entries: all entries within namespace :return: conntrack entries to delete """ # List all entries from namespace, they are already parsed # to a list of tuples: # [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 1234), # (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')] delete_entries = [] entry_index = 0 entry_number = len(entries) for rule_filter in rule_filters: while entry_index < entry_number: # Compare entry with rule comp = self._compare_entry_and_rule(rule_filter, entries[entry_index]) # Increase entry_index when entry is under rule if comp < 0: entry_index += 1 # Append entry to delete_entry if it matches with rule elif comp == 0: delete_entries.append(entries[entry_index]) entry_index += 1 # Switch to new higher rule else: break return delete_entries @staticmethod def _get_filter_from_rule(rule): """Parse the firewall rule to a tuple :param rule: firewall rule :return: a tuple of parsed information """ rule_filter = [] keys = ['ip_version', 'protocol', 'source_port', 'destination_port', 'source_ip_address', 'destination_ip_address'] for key in keys: if key in ['source_port', 'destination_port']: port_range = rule.get(key, []) if port_range: port_lower, sep, port_upper = port_range.partition(':') port_upper = port_upper if sep else port_lower port_range = [port_lower, port_upper] rule_filter.append(port_range or []) else: rule_filter.append(rule.get(key, [])) return tuple(rule_filter) @staticmethod def _compare_entry_and_rule(rule_filter, entry): """Define that the entry should be deleted or not :param rule_filter: filter that is parsed from a firewall rule ex: (4, 'tcp', 1, 2) :param entry: parsed conntrack entry, ex: (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') :return: -1 if entry is lower than rule, 0 if entry matches rule, 1 if entry is higher than rule """ ENTRY_IS_LOWER = -1 ENTRY_MATCHES = 0 ENTRY_IS_HIGHER = 1 rule_ipversion = rule_filter[0] if entry[0] < rule_ipversion: return ENTRY_IS_LOWER elif entry[0] > rule_ipversion: return ENTRY_IS_HIGHER rule_protocol = rule_filter[1] if rule_protocol: if rule_protocol == constants.PROTO_NAME_IPV6_ICMP: rule_protocol = constants.PROTO_NAME_IPV6_ICMP_LEGACY if entry[1] < rule_protocol: return ENTRY_IS_LOWER elif entry[1] > rule_protocol: return ENTRY_IS_HIGHER sport_range = rule_filter[2] if sport_range: sport_range = [int(port) for port in sport_range] if entry[2] < min(sport_range[0], sport_range[-1]): return ENTRY_IS_LOWER elif entry[2] > max(sport_range[0], sport_range[-1]): return ENTRY_IS_HIGHER dport_range = rule_filter[3] if dport_range: dport_range = [int(port) for port in dport_range] if entry[3] < min(dport_range[0], dport_range[-1]): return ENTRY_IS_LOWER elif entry[3] > max(dport_range[0], dport_range[-1]): return ENTRY_IS_HIGHER return ENTRY_MATCHES ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/firewall_agent_api.py0000664000175000017500000000570700000000000033566 0ustar00zuulzuul00000000000000# Copyright (c) 2013 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. from neutron_lib import rpc as n_rpc from oslo_config import cfg import oslo_messaging from neutron_fwaas._i18n import _ FWAAS_V1 = "v1" FWAAS_V2 = "v2" FW_L2_NOOP_DRIVER = 'noop' FWaaSOpts = [ cfg.StrOpt( 'driver', default='', help=_("Name of the FWaaS Driver")), cfg.BoolOpt( 'enabled', default=False, help=_("Enable FWaaS")), cfg.StrOpt( 'agent_version', default=FWAAS_V2, help=_("Firewall agent class")), cfg.StrOpt( 'conntrack_driver', default='conntrack', help=_("Name of the FWaaS Conntrack Driver")), cfg.StrOpt( 'firewall_l2_driver', default=FW_L2_NOOP_DRIVER, help=_("Name of the firewall l2 driver") ) ] cfg.CONF.register_opts(FWaaSOpts, 'fwaas') class FWaaSPluginApiMixin(object): """Agent side of the FWaaS agent to FWaaS Plugin RPC API.""" def __init__(self, topic, host): # NOTE(annp): Mixin class should call super super(FWaaSPluginApiMixin, self).__init__() self.host = host target = oslo_messaging.Target(topic=topic, version='1.0') self.client = n_rpc.get_client(target) def set_firewall_status(self, context, firewall_id, status): """Make a RPC to set the status of a firewall.""" cctxt = self.client.prepare() return cctxt.call(context, 'set_firewall_status', host=self.host, firewall_id=firewall_id, status=status) def firewall_deleted(self, context, firewall_id): """Make a RPC to indicate that the firewall resources are deleted.""" cctxt = self.client.prepare() return cctxt.call(context, 'firewall_deleted', host=self.host, firewall_id=firewall_id) class FWaaSAgentRpcCallbackMixin(object): """Mixin for FWaaS agent Implementations.""" def __init__(self, host): super(FWaaSAgentRpcCallbackMixin, self).__init__(host) def create_firewall(self, context, firewall, host): """Handle RPC cast from plugin to create a firewall.""" pass def update_firewall(self, context, firewall, host): """Handle RPC cast from plugin to update a firewall.""" pass def delete_firewall(self, context, firewall, host): """Handle RPC cast from plugin to delete a firewall.""" pass ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/firewall_service.py0000664000175000017500000000314300000000000033267 0ustar00zuulzuul00000000000000# Copyright 2014 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. from neutron.services import provider_configuration as provconf from oslo_config import cfg from oslo_log import log as logging from oslo_utils import importutils from neutron_fwaas._i18n import _ LOG = logging.getLogger(__name__) FIREWALL_DRIVERS = 'firewall_drivers' class FirewallService(object): """Firewall Service observer.""" def load_device_drivers(self): """Loads a single device driver for FWaaS.""" device_driver = provconf.get_provider_driver_class( cfg.CONF.fwaas.driver, FIREWALL_DRIVERS) try: driver = importutils.import_object(device_driver) LOG.debug('Loaded FWaaS device driver: %s', device_driver) return driver except ImportError: msg = _('Error importing FWaaS device driver: %s') raise ImportError(msg % device_driver) except ValueError: msg = _('Configuration error - no FWaaS device_driver specified') raise ValueError(msg) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l2/0000775000175000017500000000000000000000000027704 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l2/__init__.py0000664000175000017500000000000000000000000032003 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l2/fwaas_v2.py0000664000175000017500000004551400000000000031777 0ustar00zuulzuul00000000000000# Copyright 2017-2018 FUJITSU 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 oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging from neutron.agent import securitygroups_rpc from neutron import manager from neutron.plugins.ml2.drivers.openvswitch.agent import vlanmanager from neutron_lib.agent import l2_extension from neutron_lib import constants as nl_const from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib import rpc as n_rpc from neutron_lib.utils import net as nl_net from neutron_fwaas._i18n import _ from neutron_fwaas.common import fwaas_constants as consts from neutron_fwaas.services.firewall.service_drivers.agents import\ firewall_agent_api as api LOG = logging.getLogger(__name__) FWAAS_L2_DRIVER = 'neutron.agent.l2.firewall_drivers' SG_OVS_DRIVER = 'openvswitch' class FWaaSL2PluginApi(api.FWaaSPluginApiMixin): """L2 agent side of FWaaS agent-to-plugin RPC API""" def get_firewall_group_for_port(self, context, port_id): """Get firewall group is associated with a port""" LOG.debug("Get firewall group is associated with port %s", port_id) cctxt = self.client.prepare() return cctxt.call(context, 'get_firewall_group_for_port', port_id=port_id) def set_firewall_group_status(self, context, fwg_id, status, host): """Set the status of a group operation.""" LOG.debug("Fetch firewall group changing status") cctxt = self.client.prepare() return cctxt.call(context, 'set_firewall_group_status', fwg_id=fwg_id, status=status, host=host) def firewall_group_deleted(self, context, fwg_id, host): """Notifies the plugin that a firewall group has been deleted.""" LOG.debug("Notify to the plugin that firewall group has been deleted") cctxt = self.client.prepare() return cctxt.call(context, 'firewall_group_deleted', fwg_id=fwg_id, host=host) class FWaaSV2AgentExtension(l2_extension.L2AgentExtension): def initialize(self, connection, driver_type): """Perform Agent Extension initialization""" self.conf = cfg.CONF self.vlan_manager = vlanmanager.LocalVlanManager() fw_l2_driver_cls = self._load_l2_driver_class(driver_type) sg_enabled = securitygroups_rpc.is_firewall_enabled() sg_firewall_driver = self.conf.SECURITYGROUP.firewall_driver sg_with_ovs = sg_enabled and (sg_firewall_driver == SG_OVS_DRIVER) self.driver = manager.NeutronManager.load_class_for_provider( FWAAS_L2_DRIVER, fw_l2_driver_cls)(self.agent_api, sg_with_ovs) self.plugin_rpc = FWaaSL2PluginApi( consts.FIREWALL_PLUGIN, self.conf.host) self.start_rpc_listeners() self.fwg_map = PortFirewallGroupMap() def consume_api(self, agent_api): self.agent_api = agent_api def start_rpc_listeners(self): self.conn = n_rpc.Connection() endpoints = [self] self.conn.create_consumer(consts.FW_AGENT, endpoints, fanout=False) return self.conn.consume_in_threads() def _load_l2_driver_class(self, driver_type): driver = self.conf.fwaas.firewall_l2_driver or 'noop' if driver == api.FW_L2_NOOP_DRIVER: return driver if driver != driver_type: raise Exception( _("Firewall l2 driver: %s is not compatible"), driver_type) return driver def _is_port_layer2(self, port): """This function checks if a port belongs to a L2 case. Currently both DHCP and router ports are eliminated. """ return port and port.get('device_owner', '').startswith( nl_const.DEVICE_OWNER_COMPUTE_PREFIX) def _get_firewall_group_ports(self, fwg, host, to_delete=False): port_list = [] port_ids = fwg['del-port-ids'] if to_delete else fwg['add-port-ids'] LOG.debug("_get_fwg fwg=%(fwg)s ports=%(port)s to_delete=%(delete)s", {'fwg': fwg, 'port': port_ids, 'delete': to_delete}) for fw_port in port_ids: port_detail = fwg['port_details'].get(fw_port) if (self._is_port_layer2(port_detail) and port_detail.get('host') == host): port_list.append(port_detail) return port_list @staticmethod def _has_ports(fwg, event): """Verifying fwg has ports or not This function verify applying firewall group on ports :param fwg: a fwg object :param event: create/update firewall group or create/update/delete port :return: True if applying firewall group is fine. Otherwise is False """ if event == consts.UPDATE_FWG and 'last-port' in fwg: return not fwg['last-port'] else: return bool(fwg['ports']) @staticmethod def _has_policy(fwg): """Verifying fwg has policy or not""" return bool(fwg['ingress_firewall_policy_id'] or fwg['egress_firewall_policy_id']) def _compute_status(self, fwg, result, event=consts.CREATE_FWG): """Compute a status of specified firewall group for update Validates 'ACTIVE', 'DOWN', 'INACTIVE', 'ERROR' and None as follows: - "ERROR" : if result is not True - "ACTIVE" : admin_state_up is True and exists ports - "INACTIVE" : admin_state_up is True and with no ports - "DOWN" : admin_state_up is False - None : In case of 'delete_firewall_group' """ if not result: return nl_const.ERROR if not fwg['admin_state_up']: return nl_const.DOWN if event == consts.DELETE_FWG: # This firewall_group will be deleted. No need to update status. return if (self._has_ports(fwg, event) and self._has_policy(fwg)): return nl_const.ACTIVE return nl_const.INACTIVE def _get_network_and_segmentation_id(self, fwg_port): port_id = fwg_port.get('port_id', fwg_port.get('id')) port_details = fwg_port.get('port_details') if port_details: target = port_details.get(port_id) if target: return target.get('network_id'), target.get('segmentation_id') return return fwg_port.get('network_id'), fwg_port.get('segmentation_id') def _add_local_vlan_to_ports(self, fwg_ports): """Add local VLAN to ports if found This function tries to add local VLAN related to ports. """ ports_with_lvlan = [] for fwg_port in fwg_ports: try: network_id, segm_id = self._get_network_and_segmentation_id( fwg_port) l_vlan = self.vlan_manager.get(network_id, segm_id).vlan fwg_port['lvlan'] = int(l_vlan) except vlanmanager.MappingNotFound: LOG.warning("No Local VLAN found in network %s", network_id) # NOTE(yushiro): We ignore this exception because we should send # all selected ports to driver layer. It depends on driver's # behavior whether it occurs an error with no local VLAN or not. ports_with_lvlan.append(fwg_port) return ports_with_lvlan def _apply_fwg_rules(self, fwg, ports, event=consts.UPDATE_FWG): """This function invokes the driver create/update routine. """ # Set firewall group status; will be overwritten if call to driver # fails. if event in [consts.CREATE_FWG, consts.UPDATE_FWG]: ports_for_driver = self._add_local_vlan_to_ports(ports) else: ports_for_driver = ports # apply firewall group to driver try: if event == consts.UPDATE_FWG: self.driver.update_firewall_group(ports_for_driver, fwg) elif event == consts.DELETE_FWG: self.driver.delete_firewall_group(ports_for_driver, fwg) elif event == consts.CREATE_FWG: self.driver.create_firewall_group(ports_for_driver, fwg) except f_exc.FirewallInternalDriverError: msg = _("FWaaS driver error in %(event)s_firewall_group " "for firewall group: %(fwg_id)s") LOG.exception(msg, {'event': event, 'fwg_id': fwg['id']}) return False return True def _send_fwg_status(self, context, fwg_id, status, host): """Send firewall group's status to plugin. :returns: True if no exception occurred otherwise False :rtype: boolean """ try: self.plugin_rpc.set_firewall_group_status( context, fwg_id, status, host) LOG.debug("Successfully sent status(%s) for firewall_group(%s)", status, fwg_id) except Exception: msg = _("Failed to send status for firewall_group(%s)") LOG.exception(msg, fwg_id) def _create_firewall_group(self, context, fwg, host, event=consts.CREATE_FWG): """Handles RPC from plugin to create a firewall group. """ add_ports = self._get_firewall_group_ports(fwg, host) if not add_ports: status = nl_const.INACTIVE else: ret = self._apply_fwg_rules(fwg, add_ports, event) # cleanup port_map for port in add_ports: self.fwg_map.remove_port(port) status = self._compute_status(fwg, ret, event) for port in add_ports: self.fwg_map.set_port_fwg(port, fwg) # Update status of firewall group which is associated with ports # after updating. self._send_fwg_status(context, fwg['id'], status, host) def _delete_firewall_group(self, context, fwg, host, event=consts.DELETE_FWG): """Handles RPC from plugin to delete a firewall group. """ del_ports = self._get_firewall_group_ports(fwg, host, to_delete=True) if not del_ports: return # cleanup all flows of del_ports ret = self._apply_fwg_rules(fwg, del_ports, event=consts.DELETE_FWG) del_port_ids = [] for port in del_ports: del_port_ids.append(port['id']) self.fwg_map.remove_port(port) if event == consts.DELETE_FWG: self.fwg_map.remove_fwg(fwg) self.plugin_rpc.firewall_group_deleted( context, fwg['id'], host=self.conf.host) else: status = self._compute_status(fwg, ret, event) self._send_fwg_status(context, fwg['id'], status, self.conf.host) @lockutils.synchronized('fwg') def create_firewall_group(self, context, firewall_group, host): """Handles create firewall group event""" # TODO(chandanc): Fix agent RPC endpoint to remove host arg host = cfg.CONF.host with self.driver.defer_apply(): try: self._create_firewall_group(context, firewall_group, host) except Exception as exc: LOG.exception( "Exception caught in create_firewall_group %s", exc) self._send_fwg_status(context, firewall_group['id'], status=nl_const.ERROR, host=host) @lockutils.synchronized('fwg') def delete_firewall_group(self, context, firewall_group, host): """Handles delete firewall group event""" # TODO(chandanc): Fix agent RPC endpoint to remove host arg host = cfg.CONF.host with self.driver.defer_apply(): try: self._delete_firewall_group(context, firewall_group, host) except Exception as exc: LOG.exception( "Exception caught in delete_firewall_group %s", exc) self._send_fwg_status(context, firewall_group['id'], status=nl_const.ERROR, host=host) @lockutils.synchronized('fwg') def update_firewall_group(self, context, firewall_group, host): """Handles update firewall group event""" # TODO(chandanc): Fix agent RPC endpoint to remove host arg host = cfg.CONF.host with self.driver.defer_apply(): try: self._delete_firewall_group( context, firewall_group, host, event=consts.UPDATE_FWG) self._create_firewall_group( context, firewall_group, host, event=consts.UPDATE_FWG) except Exception as exc: LOG.exception( "Exception caught in update_firewall_group %s", exc) self._send_fwg_status(context, firewall_group['id'], status=nl_const.ERROR, host=host) @lockutils.synchronized('fwg-port') def handle_port(self, context, port): """Handle port update event""" # Check if port is trusted and called at once. if nl_net.is_port_trusted(port) and not self.fwg_map.get_port(port): self._add_rule_for_trusted_port(port) self.fwg_map.set_port(port) return if not self._is_port_layer2(port): return # check if port is already assigned to a fwg if self.fwg_map.get_port_fwg(port): return fwg = self.plugin_rpc.get_firewall_group_for_port( context, port.get('port_id')) if not fwg: LOG.info("Firewall group applied to port %s is " "not available on server.", port['port_id']) return ret = self._apply_fwg_rules(fwg, [port]) status = self._compute_status(fwg, ret, event=consts.HANDLE_PORT) self.fwg_map.set_port_fwg(port, fwg) self._send_fwg_status( context, fwg_id=fwg['id'], status=status, host=self.conf.host) def _add_rule_for_trusted_port(self, port): self._add_local_vlan_to_ports([port]) self.driver.process_trusted_ports([port]) def _delete_rule_for_trusted_port(self, port): self.driver.remove_trusted_ports([port['port_id']]) def delete_port(self, context, port): """This is being called when a port is deleted by the agent. """ # delete_port should be handled only unbound timing for a port. # If 'vif_port' is included in the port dict, this is called after # deleted the port and should be ignored. if 'vif_port' in port: return port = self.fwg_map.get_port(port) if port and nl_net.is_port_trusted(port): self._delete_rule_for_trusted_port(port) self.fwg_map.remove_port(port) return if not self._is_port_layer2(port): return fwg = self.fwg_map.get_port_fwg(port) if not fwg: LOG.info("Firewall group associated to port %(port_id)s is " "not available on server.", {'port_id': port['port_id']}) return ret = self._apply_fwg_rules(fwg, [port], event=consts.DELETE_FWG) port_id = self.fwg_map.port_id(port) if port_id in fwg['ports']: fwg['ports'].remove(port_id) # update the fwg dict to known_fwgs self.fwg_map.set_fwg(fwg) self.fwg_map.remove_port(port) status = self._compute_status(fwg, ret, event=consts.DELETE_PORT) self._send_fwg_status(context, fwg['id'], status, self.conf.host) class PortFirewallGroupMap(object): """Store relations between Port and Firewall Group and trusted port This map is used in deleting firewall_group because the firewall_group has been deleted at that time. Therefore, it is impossible to refer 'ports'. This map enables to refer 'ports' for specified firewall_group. Furthermore, it is necessary to check 'device_owner' for trusted port, this Map also stores trusted port data. """ def __init__(self): self.known_fwgs = {} self.port_fwg = {} self.port_detail = {} # TODO(yushiro): If agent is restarted, this map doesn't have any # information. Need to consider map initialization in __init__() def port_id(self, port): return (port if isinstance(port, str) else port.get('port_id', port.get('id'))) def get_fwg(self, fwg_id): return self.known_fwgs.get(fwg_id) def set_fwg(self, fwg): self.known_fwgs[fwg['id']] = fwg def get_port(self, port): return self.port_detail.get(self.port_id(port)) def get_port_fwg(self, port): fwg_id = self.port_fwg.get(self.port_id(port)) if fwg_id: return self.get_fwg(fwg_id) def set_port(self, port): """Add a new port into port_detail""" port_id = self.port_id(port) self.port_detail[port_id] = port def set_port_fwg(self, port, fwg): """Add a new port into fwg['ports']""" port_id = self.port_id(port) # Update fwg['ports'] data fwg['ports'] = list(set(fwg['ports'] + [port_id])) # Update fwg_id -> firewall_group data self.known_fwgs[fwg['id']] = fwg # Update port_id -> port data self.port_detail[port_id] = port # Update port_id -> firewall_group_id relation self.port_fwg[port_id] = fwg['id'] def remove_port(self, port): """Remove port from fwg['ports'] and port_fwg dictionary When removing 'port' from several cases, the port should be removed from this map. """ port_id = self.port_id(port) # Check if 'port_id' has registered in port_fwg dictionary. # Update firewall_group if port_id in self.port_fwg: fwg_id = self.port_fwg.get(port_id) if not fwg_id: # This case is trusted port. Try to delete port_detail dict try: del self.port_detail[port_id] except KeyError: pass return new_fwg = self.known_fwgs[fwg_id] new_fwg['ports'] = [p for p in new_fwg['ports'] if p != port_id] self.known_fwgs[fwg_id] = new_fwg del self.port_fwg[port_id] del self.port_detail[port_id] def remove_fwg(self, fwg): """Remove firewall_group from known_fwgs dictionary When removing firewall_group, it should be removed from this map """ if fwg['id'] in self.known_fwgs: del self.known_fwgs[fwg['id']] ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/0000775000175000017500000000000000000000000031564 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/__init__.py0000664000175000017500000000000000000000000033663 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021700000000000011455 xustar0000000000000000121 path=neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent_v2.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_0000664000175000017500000005657500000000000034073 0ustar00zuulzuul00000000000000# Copyright (c) 2016 # 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.agent.linux import ip_lib from neutron_lib.agent import l3_extension from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as fw_ext from neutron_lib import rpc as n_rpc from oslo_config import cfg from oslo_log import helpers as log_helpers from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants from neutron_fwaas.common import resources as f_resources from neutron_fwaas.services.firewall.service_drivers.agents import\ firewall_agent_api as api from neutron_fwaas.services.firewall.service_drivers.agents import\ firewall_service LOG = logging.getLogger(__name__) class FWaaSL3PluginApi(api.FWaaSPluginApiMixin): """Agent side of the FWaaS agent-to-plugin RPC API.""" def __init__(self, topic, host): super(FWaaSL3PluginApi, self).__init__(topic, host) def get_firewall_groups_for_project(self, context, **kwargs): """Fetches a project's firewall groups from the plugin.""" LOG.debug("Fetch firewall groups from plugin") cctxt = self.client.prepare() return cctxt.call(context, 'get_firewall_groups_for_project', host=self.host) def get_projects_with_firewall_groups(self, context, **kwargs): """Fetches from the plugin all projects that have firewall groups configured. """ LOG.debug("Fetch from plugin projects that have firewall groups " "configured") cctxt = self.client.prepare() return cctxt.call(context, 'get_projects_with_firewall_groups', host=self.host) def firewall_group_deleted(self, context, fwg_id, **kwargs): """Notifies the plugin that a firewall group has been deleted.""" LOG.debug("Notify plugin that firewall group has been deleted") cctxt = self.client.prepare() return cctxt.call(context, 'firewall_group_deleted', fwg_id=fwg_id, host=self.host) def set_firewall_group_status(self, context, fwg_id, status, **kwargs): """Sets firewall group's status on the plugin.""" LOG.debug("Set firewall groups from plugin") cctxt = self.client.prepare() return cctxt.call(context, 'set_firewall_group_status', fwg_id=fwg_id, status=status, host=self.host) class FWaaSL3AgentExtension(l3_extension.L3AgentExtension): """FWaaS agent extension.""" SUPPORTED_RESOURCE_TYPES = [f_resources.FIREWALL_GROUP, f_resources.FIREWALL_POLICY, f_resources.FIREWALL_RULE] def initialize(self, connection, driver_type): self._register_rpc_consumers(connection) def consume_api(self, agent_api): LOG.debug("FWaaS consume_api call occurred with %s", agent_api) self.agent_api = agent_api def _register_rpc_consumers(self, connection): # TODO(njohnston): Add RPC consumer connection loading here. pass def start_rpc_listeners(self, host, conf): self.endpoints = [self] self.conn = n_rpc.Connection() self.conn.create_consumer( fwaas_constants.FW_AGENT, self.endpoints, fanout=False) return self.conn.consume_in_threads() def __init__(self, host, conf): LOG.debug("Initializing firewall group agent") self.agent_api = None self.neutron_service_plugins = None self.conf = conf self.fwaas_enabled = cfg.CONF.fwaas.enabled self.start_rpc_listeners(host, conf) # None means l3-agent has no information on the server # configuration due to the lack of RPC support. if self.neutron_service_plugins is not None: fwaas_plugin_configured = (fwaas_constants.FIREWALL in self.neutron_service_plugins) if fwaas_plugin_configured and not self.fwaas_enabled: msg = ("FWaaS plugin is configured in the server side, but " "FWaaS is disabled in L3-agent.") LOG.error(msg) raise SystemExit(1) self.fwaas_enabled = self.fwaas_enabled and fwaas_plugin_configured if self.fwaas_enabled: # NOTE: Temp location for creating service and loading driver self.fw_service = firewall_service.FirewallService() self.fwaas_driver = self.fw_service.load_device_drivers() self.services_sync_needed = False self.fwplugin_rpc = FWaaSL3PluginApi(fwaas_constants.FIREWALL_PLUGIN, host) super(FWaaSL3AgentExtension, self).__init__() @property def _local_namespaces(self): local_ns_list = ip_lib.list_network_namespaces() return local_ns_list def _has_port_insertion_fields(self, firewall_group): """The presence of the 'add-port-ids' key in the firewall group dict shows we are using the current version of the plugin. If this key is absent, we are in an upgrade and message is from an older version of the plugin. """ return 'add-port-ids' in firewall_group def _get_firewall_group_ports(self, context, firewall_group, to_delete=False, require_new_plugin=False): """Returns in-namespace ports, either from firewall group dict if ports update or from project routers otherwise if only policies update. NOTE: Vernacular move from "tenant" to "project" doesn't yet appear as a key in router or firewall group objects. """ fwg_port_ids = [] if self._has_port_insertion_fields(firewall_group): if to_delete: fwg_port_ids = firewall_group['del-port-ids'] else: fwg_port_ids = firewall_group['add-port-ids'] if not require_new_plugin and not fwg_port_ids: routers = self.agent_api.get_routers_in_project( firewall_group['tenant_id']) for router in routers: if router.router['tenant_id'] == firewall_group['tenant_id']: fwg_port_ids.extend([p['id'] for p in router.internal_ports]) # Return in-namespace port objects. return self._get_in_ns_ports(fwg_port_ids) def _get_in_ns_ports(self, port_ids): """Get ports in namespace by their IDs. Returns port objects in the local namespace, along with their router_info. :param port_ids: IDs of router ports (set, list or tuple) """ in_ns_ports = {} # This will be converted to a list later. if port_ids and self.agent_api: for port_id in port_ids: # This fetched router_info is guaranteed to be in_namespace. router_info = self.agent_api.get_router_hosting_port(port_id) if router_info: if router_info in in_ns_ports: in_ns_ports[router_info].append(port_id) else: in_ns_ports[router_info] = [port_id] return list(in_ns_ports.items()) def _invoke_driver_for_sync_from_plugin(self, ctx, ports, firewall_group): """Call driver to sync firewall group. Calls the FWaaS driver's delete_firewall_group method if firewall group has status of PENDING_DELETE; calls driver's update_firewall_group method for all other statuses. Both of these methods are idempotent. :param ctx: RPC context :param ports: IDs of ports associated with a firewall group (set, list or tuple) :param firewall_group: Dictionary describing the firewall group object """ port_list = self._get_in_ns_ports(ports) if firewall_group['status'] == nl_constants.PENDING_DELETE: try: self.fwaas_driver.delete_firewall_group( self.conf.agent_mode, port_list, firewall_group) self.fwplugin_rpc.firewall_group_deleted( ctx, firewall_group['id']) except fw_ext.FirewallInternalDriverError: msg = ("FWaaS driver error on %(status)s " "for firewall group: %(fwg_id)s") LOG.exception(msg, {'status': firewall_group['status'], 'fwg_id': firewall_group['id']}) self.fwplugin_rpc.set_firewall_group_status( ctx, firewall_group['id'], nl_constants.ERROR) else: # PENDING_UPDATE, PENDING_CREATE, ... # Prepare firewall group status to return to plugin; may be # overwritten if call to driver fails. if firewall_group['admin_state_up']: status = nl_constants.ACTIVE else: status = nl_constants.DOWN # Call the driver. try: self.fwaas_driver.update_firewall_group( self.conf.agent_mode, port_list, firewall_group) except fw_ext.FirewallInternalDriverError: msg = ("FWaaS driver error on %(status)s for firewall " "group: %(fwg_id)s") LOG.exception(msg, {'status': firewall_group['status'], 'fwg_id': firewall_group['id']}) status = nl_constants.ERROR if firewall_group['status'] != status: # Notify the plugin of firewall group's status. self.fwplugin_rpc.set_firewall_group_status( ctx, firewall_group['id'], status) def _process_router_update(self, updated_router): """If a new or existing router in the local namespace is updated, queries the plugin to get the firewall groups for the project in question and then sees if the router has any ports for any firewall group that is configured for that project. If so, installs firewall group rules on the requested ports on this router. """ LOG.debug("Process router update, router_id: %s tenant: %s.", updated_router['id'], updated_router['tenant_id']) router_id = updated_router['id'] if not self.agent_api.is_router_in_namespace(router_id): return # Get the firewall groups for the new router's project. # NOTE: Vernacular move from "tenant" to "project" doesn't yet appear # as a key in router or firewall group objects. ctx = context.Context('', updated_router['tenant_id']) fwg_list = self.fwplugin_rpc.get_firewall_groups_for_project(ctx) if nl_constants.INTERFACE_KEY not in updated_router: return # Apply a firewall group, as requested, to ports on the new router. all_router_ports = set( p['id'] for p in updated_router[nl_constants.INTERFACE_KEY] ) processed_ports = set() for firewall_group in fwg_list: if not self._has_port_insertion_fields(firewall_group): continue ports_to_process = (set(firewall_group['add-port-ids'] + firewall_group['del-port-ids']) & all_router_ports) # ensure no port in router is associated with the firewall group if not ports_to_process: continue # A port can have at most one firewall group. port_ids_to_exclude = ports_to_process & processed_ports if port_ids_to_exclude: LOG.warning("Port(s) %s is associated with " "more than one firewall group(s).", port_ids_to_exclude) ports_to_process -= port_ids_to_exclude self._invoke_driver_for_sync_from_plugin( ctx, ports_to_process, firewall_group) processed_ports |= ports_to_process def add_router(self, context, new_router): """Handles agent restart and router add. Fetches firewall groups from plugin and updates driver. """ if not self.fwaas_enabled: return try: self._process_router_update(new_router) except Exception: LOG.exception("FWaaS router add RPC info call failed for %s", new_router['id']) self.services_sync_needed = True def update_router(self, context, updated_router): """Handles agent restart and router update. Fetches firewall groups from plugin and updates driver. """ if not self.fwaas_enabled: return try: self._process_router_update(updated_router) except Exception: # TODO(njohnston): This repr should be replaced. LOG.exception( "FWaaS router update RPC info call failed for %s", repr(updated_router)) self.services_sync_needed = True def delete_router(self, context, new_router): """Handles router deletion. There is basically nothing to do for this in the context of FWaaS with an IPTables driver; the namespace will already have been deleted, taking the IPTables rules with it. """ # TODO(njohnston): When another firewall driver is implemented, look at # expanding this out so that the driver can handle deletion calls. pass def update_network(self, context, data): pass def process_services_sync(self, ctx): """Syncs with plugin and applies the sync data. """ if not self.services_sync_needed or not self.fwaas_enabled: return try: # Fetch from the plugin the list of projects with firewall groups. project_ids = \ self.fwplugin_rpc.get_projects_with_firewall_groups(ctx) LOG.debug("Projects with firewall groups: %s", ', '.join(project_ids)) for project_id in project_ids: ctx = context.Context('', project_id) fwg_list = \ self.fwplugin_rpc.get_firewall_groups_for_project(ctx) for firewall_group in fwg_list: if firewall_group['status'] == nl_constants.PENDING_DELETE: self.delete_firewall_group(ctx, firewall_group, self.host) # No need to apply sync data for ACTIVE firewall group. elif firewall_group['status'] != nl_constants.ACTIVE: self.update_firewall_group(ctx, firewall_group, self.host) self.services_sync_needed = False except Exception: LOG.exception("Failed FWaaS process services sync.") self.services_sync_needed = True @log_helpers.log_method_call def create_firewall_group(self, context, firewall_group, host): """Handles RPC from plugin to create a firewall group. """ # Get the in-namespace ports to which to add the firewall group. ports_for_fwg = self._get_firewall_group_ports(context, firewall_group) if not ports_for_fwg: return LOG.debug("Create firewall group %(fwg_id)s on ports: %(ports)s", {'fwg_id': firewall_group['id'], 'ports': ', '.join([p for ri_ports in ports_for_fwg for p in ri_ports[1]])}) # Set firewall group status; will be overwritten if call to driver # fails. if firewall_group['admin_state_up']: status = nl_constants.ACTIVE else: status = nl_constants.DOWN # Call the driver. try: self.fwaas_driver.create_firewall_group(self.conf.agent_mode, ports_for_fwg, firewall_group) except fw_ext.FirewallInternalDriverError: msg = ("FWaaS driver error in create_firewall_group " "for firewall group: %(fwg_id)s") LOG.exception(msg, {'fwg_id': firewall_group['id']}) status = nl_constants.ERROR # Send firewall group's status to plugin. try: self.fwplugin_rpc.set_firewall_group_status(context, firewall_group['id'], status) except Exception: msg = ("FWaaS RPC failure in create_firewall_group " "for firewall group: %(fwg_id)s") LOG.exception(msg, {'fwg_id': firewall_group['id']}) self.services_sync_needed = True @log_helpers.log_method_call def update_firewall_group(self, context, firewall_group, host): """Handles RPC from plugin to update a firewall group. """ # Initialize firewall group status. status = "" # Get the list of in-namespace ports from which to delete the firewall # group. del_fwg_ports = self._get_firewall_group_ports( context, firewall_group, to_delete=True, require_new_plugin=True) add_fwg_ports = self._get_firewall_group_ports(context, firewall_group) port_ids = (firewall_group.get('del-port-ids') + firewall_group.get('add-port-ids')) if port_ids and not (del_fwg_ports or add_fwg_ports): LOG.debug("All ports are not router port." "No need to update firewall driver.") return # Remove firewall group from ports if requested. if del_fwg_ports: fw_ports = [p for ri_port in del_fwg_ports for p in ri_port[1]] LOG.debug("Update (delete) firewall group %(fwg_id)s on ports: " "%(ports)s", {'fwg_id': firewall_group['id'], 'ports': ', '.join(fw_ports)}) # Set firewall group's status; will be overwritten if call to # driver fails. if firewall_group['admin_state_up']: status = nl_constants.ACTIVE if firewall_group['last-port']: status = nl_constants.INACTIVE else: status = nl_constants.DOWN # Call the driver. try: self.fwaas_driver.delete_firewall_group(self.conf.agent_mode, del_fwg_ports, firewall_group) except fw_ext.FirewallInternalDriverError: msg = ("FWaaS driver error in update_firewall_group " "(add) for firewall group: %s") LOG.exception(msg, firewall_group['id']) status = nl_constants.ERROR # Handle the add router and/or rule, policy, firewall group attribute # updates. if status not in (nl_constants.ERROR, nl_constants.INACTIVE): if add_fwg_ports: fw_ports = [p for ri_port in add_fwg_ports for p in ri_port[1]] LOG.debug("Update (create) firewall group %(fwg_id)s on " "ports: %(ports)s", {'fwg_id': firewall_group['id'], 'ports': ', '.join(fw_ports)}) # Set firewall group status, which will be overwritten if call # to driver fails. if firewall_group['admin_state_up']: status = nl_constants.ACTIVE else: status = nl_constants.DOWN # Call the driver. try: self.fwaas_driver.update_firewall_group( self.conf.agent_mode, add_fwg_ports, firewall_group) except fw_ext.FirewallInternalDriverError: msg = ("FWaaS driver error in update_firewall_group " "for firewall group: %s") LOG.exception(msg, firewall_group['id']) status = nl_constants.ERROR elif not status: # if status not set by now, set it to INACTIVE status = nl_constants.INACTIVE # Return status to plugin. try: self.fwplugin_rpc.set_firewall_group_status(context, firewall_group['id'], status) except Exception: LOG.exception("FWaaS RPC failure in update_firewall_group " "for firewall group: %s", firewall_group['id']) self.services_sync_needed = True @log_helpers.log_method_call def delete_firewall_group(self, context, firewall_group, host): """Handles RPC from plugin to delete a firewall group. """ ports_for_fwg = self._get_firewall_group_ports(context, firewall_group, to_delete=True) if not ports_for_fwg: return fw_ports = [p for ri_ports in ports_for_fwg for p in ri_ports[1]] LOG.debug("Delete firewall group %(fwg_id)s on ports: %(ports)s", {'fwg_id': firewall_group['id'], 'ports': ', '.join(fw_ports)}) # Set the firewall group's status to return to plugin; status may be # overwritten if call to driver fails. if firewall_group['admin_state_up']: status = nl_constants.ACTIVE else: status = nl_constants.DOWN try: self.fwaas_driver.delete_firewall_group(self.conf.agent_mode, ports_for_fwg, firewall_group) # Call the driver. except fw_ext.FirewallInternalDriverError: LOG.exception("FWaaS driver error in delete_firewall_group " "for firewall group: %s", firewall_group['id']) status = nl_constants.ERROR # Notify plugin of deletion or return firewall group's status to # plugin, as appropriate. try: if status in [nl_constants.ACTIVE, nl_constants.DOWN]: self.fwplugin_rpc.firewall_group_deleted(context, firewall_group['id']) else: self.fwplugin_rpc.set_firewall_group_status(context, firewall_group['id'], status) except Exception: LOG.exception("FWaaS RPC failure in delete_firewall_group " "for firewall group: %s", firewall_group['id']) self.services_sync_needed = True def ha_state_change(self, context, data): pass class L3WithFWaaS(FWaaSL3AgentExtension): def __init__(self, conf=None): if conf: self.conf = conf else: self.conf = cfg.CONF super(L3WithFWaaS, self).__init__(host=self.conf.host, conf=self.conf) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/firewall/service_drivers/driver_api.py0000664000175000017500000004673600000000000030624 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Juniper Networks, Inc. All rights reserved. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import abc import copy from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import constants as nl_constants from neutron_lib.db import api as db_api from neutron_lib.plugins import directory from oslo_log import log as logging from neutron_fwaas.common import fwaas_constants as const from neutron_fwaas.db.firewall.v2 import firewall_db_v2 LOG = logging.getLogger(__name__) class FirewallDriver(object, metaclass=abc.ABCMeta): """Firewall v2 interface for driver That driver interface does not persist Firewall v2 data in any database. The driver needs to do it by itself. """ def __init__(self, service_plugin): self.service_plugin = service_plugin @property def _core_plugin(self): return directory.get_plugin() def is_supported_l2_port(self, port): return False def is_supported_l3_port(self, port): return False # Firewall Group @abc.abstractmethod def create_firewall_group(self, context, firewall_group): pass @abc.abstractmethod def delete_firewall_group(self, context, id): pass @abc.abstractmethod def get_firewall_group(self, context, id, fields=None): pass @abc.abstractmethod def get_firewall_groups(self, context, filters=None, fields=None): pass @abc.abstractmethod def update_firewall_group(self, context, id, firewall_group): pass # Firewall Policy @abc.abstractmethod def create_firewall_policy(self, context, firewall_policy): pass @abc.abstractmethod def delete_firewall_policy(self, context, id): pass @abc.abstractmethod def get_firewall_policy(self, context, id, fields=None): pass @abc.abstractmethod def get_firewall_policies(self, context, filters=None, fields=None): pass @abc.abstractmethod def update_firewall_policy(self, context, id, firewall_policy): pass # Firewall Rule @abc.abstractmethod def create_firewall_rule(self, context, firewall_rule): pass @abc.abstractmethod def delete_firewall_rule(self, context, id): pass @abc.abstractmethod def get_firewall_rule(self, context, id, fields=None): pass @abc.abstractmethod def get_firewall_rules(self, context, filters=None, fields=None): pass @abc.abstractmethod def update_firewall_rule(self, context, id, firewall_rule): pass @abc.abstractmethod def insert_rule(self, context, policy_id, rule_info): pass @abc.abstractmethod def remove_rule(self, context, policy_id, rule_info): pass class FirewallDriverDBMixin(FirewallDriver, metaclass=abc.ABCMeta): """FirewallDriverDB mixin to provision the database on behalf of the driver That driver interface persists Firewall data in its database and forwards the result to pre and post commit methods. """ def __init__(self, *args, **kwargs): super(FirewallDriverDBMixin, self).__init__(*args, **kwargs) self.firewall_db = firewall_db_v2.FirewallPluginDb() @staticmethod @db_api.CONTEXT_READER def _update_resource_status(context, resource_type, resource_dict): context.session.query(resource_type).\ filter_by(id=resource_dict['id']).\ update({'status': resource_dict['status']}) # Firewall Group def create_firewall_group(self, context, firewall_group): request_body = firewall_group with db_api.CONTEXT_WRITER.using(context): firewall_group = self.firewall_db.create_firewall_group( context, firewall_group) self.create_firewall_group_precommit(context, firewall_group) self._update_resource_status(context, firewall_db_v2.FirewallGroup, firewall_group) self.create_firewall_group_postcommit(context, firewall_group) payload = events.DBEventPayload(context=context, resource_id=firewall_group['id'], request_body=request_body, states=(firewall_group,)) registry.publish( const.FIREWALL_GROUP, events.AFTER_CREATE, self, payload=payload) return firewall_group @abc.abstractmethod def create_firewall_group_precommit(self, context, firewall_group): pass @abc.abstractmethod def create_firewall_group_postcommit(self, context, firewall_group): pass def delete_firewall_group(self, context, id): firewall_group = self.firewall_db.get_firewall_group(context, id) if firewall_group['status'] == nl_constants.PENDING_DELETE: firewall_group['status'] = nl_constants.ERROR self.delete_firewall_group_precommit(context, firewall_group) if firewall_group['status'] != nl_constants.PENDING_DELETE: # lets driver deleting firewall group later self.firewall_db.delete_firewall_group(context, id) self.delete_firewall_group_postcommit(context, firewall_group) payload = events.DBEventPayload(context=context, resource_id=id, states=(firewall_group,)) registry.publish( const.FIREWALL_GROUP, events.AFTER_DELETE, self, payload=payload) @abc.abstractmethod def delete_firewall_group_precommit(self, context, firewall_group): pass @abc.abstractmethod def delete_firewall_group_postcommit(self, context, firewall_group): pass def get_firewall_group(self, context, id, fields=None): return self.firewall_db.get_firewall_group(context, id, fields=fields) def get_firewall_groups(self, context, filters=None, fields=None): return self.firewall_db.get_firewall_groups(context, filters, fields) def update_firewall_group(self, context, id, firewall_group_delta): old_firewall_group = self.firewall_db.get_firewall_group(context, id) new_firewall_group = copy.deepcopy(old_firewall_group) new_firewall_group.update(firewall_group_delta) self.update_firewall_group_precommit(context, old_firewall_group, new_firewall_group) firewall_group_delta['status'] = new_firewall_group['status'] firewall_group = self.firewall_db.update_firewall_group( context, id, firewall_group_delta) self.update_firewall_group_postcommit(context, old_firewall_group, firewall_group) payload = events.DBEventPayload(context=context, resource_id=id, states=(old_firewall_group, new_firewall_group)) registry.publish( const.FIREWALL_GROUP, events.AFTER_UPDATE, self, payload=payload) return firewall_group @abc.abstractmethod def update_firewall_group_precommit(self, context, old_firewall_group, new_firewall_group): pass @abc.abstractmethod def update_firewall_group_postcommit(self, context, old_firewall_group, new_firewall_group): pass # Firewall Policy def create_firewall_policy(self, context, firewall_policy): request_body = firewall_policy with db_api.CONTEXT_WRITER.using(context): firewall_policy = self.firewall_db.create_firewall_policy( context, firewall_policy) self.create_firewall_policy_precommit(context, firewall_policy) self.create_firewall_policy_postcommit(context, firewall_policy) payload = events.DBEventPayload(context=context, resource_id=firewall_policy['id'], request_body=request_body, states=(firewall_policy,)) registry.publish( const.FIREWALL_POLICY, events.AFTER_CREATE, self, payload=payload) return firewall_policy @abc.abstractmethod def create_firewall_policy_precommit(self, context, firewall_policy): pass @abc.abstractmethod def create_firewall_policy_postcommit(self, context, firewall_policy): pass def delete_firewall_policy(self, context, id): firewall_policy = self.firewall_db.get_firewall_policy(context, id) self.delete_firewall_policy_precommit(context, firewall_policy) self.firewall_db.delete_firewall_policy(context, id) self.delete_firewall_policy_postcommit(context, firewall_policy) payload = events.DBEventPayload(context=context, resource_id=id, states=(firewall_policy,)) registry.publish( const.FIREWALL_POLICY, events.AFTER_UPDATE, self, payload=payload) @abc.abstractmethod def delete_firewall_policy_precommit(self, context, firewall_policy): pass @abc.abstractmethod def delete_firewall_policy_postcommit(self, context, firewall_policy): pass def get_firewall_policy(self, context, id, fields=None): return self.firewall_db.get_firewall_policy(context, id, fields) def get_firewall_policies(self, context, filters=None, fields=None): return self.firewall_db.get_firewall_policies(context, filters, fields) def update_firewall_policy(self, context, id, firewall_policy_delta): old_firewall_policy = self.firewall_db.get_firewall_policy(context, id) new_firewall_policy = copy.deepcopy(old_firewall_policy) new_firewall_policy.update(firewall_policy_delta) self.update_firewall_policy_precommit(context, old_firewall_policy, new_firewall_policy) firewall_policy = self.firewall_db.update_firewall_policy( context, id, firewall_policy_delta) self.update_firewall_policy_postcommit(context, old_firewall_policy, firewall_policy) payload = events.DBEventPayload(context=context, resource_id=id, states=(firewall_policy,)) registry.publish( const.FIREWALL_POLICY, events.AFTER_UPDATE, self, payload=payload) return firewall_policy @abc.abstractmethod def update_firewall_policy_precommit(self, context, old_firewall_policy, new_firewall_policy): pass @abc.abstractmethod def update_firewall_policy_postcommit(self, context, old_firewall_policy, new_firewall_policy): pass # Firewall Rule def create_firewall_rule(self, context, firewall_rule): request_body = firewall_rule with db_api.CONTEXT_WRITER.using(context): firewall_rule = self.firewall_db.create_firewall_rule( context, firewall_rule) self.create_firewall_rule_precommit(context, firewall_rule) self.create_firewall_rule_postcommit(context, firewall_rule) payload = events.DBEventPayload(context=context, resource_id=firewall_rule['id'], request_body=request_body, states=(firewall_rule,)) registry.publish( const.FIREWALL_RULE, events.AFTER_CREATE, self, payload=payload) return firewall_rule @abc.abstractmethod def create_firewall_rule_precommit(self, context, firewall_rule): pass @abc.abstractmethod def create_firewall_rule_postcommit(self, context, firewall_rule): pass def delete_firewall_rule(self, context, id): firewall_rule = self.firewall_db.get_firewall_rule(context, id) self.delete_firewall_rule_precommit(context, firewall_rule) self.firewall_db.delete_firewall_rule(context, id) self.delete_firewall_rule_postcommit(context, firewall_rule) payload = events.DBEventPayload(context=context, resource_id=id, states=(firewall_rule,)) registry.publish( const.FIREWALL_RULE, events.AFTER_DELETE, self, payload=payload) @abc.abstractmethod def delete_firewall_rule_precommit(self, context, firewall_rule): pass @abc.abstractmethod def delete_firewall_rule_postcommit(self, context, firewall_rule): pass def get_firewall_rule(self, context, id, fields=None): return self.firewall_db.get_firewall_rule(context, id, fields) def get_firewall_rules(self, context, filters=None, fields=None): return self.firewall_db.get_firewall_rules(context, filters, fields) def update_firewall_rule(self, context, id, firewall_rule_delta): old_firewall_rule = self.firewall_db.get_firewall_rule(context, id) new_firewall_rule = copy.deepcopy(old_firewall_rule) new_firewall_rule.update(firewall_rule_delta) self.update_firewall_rule_precommit(context, old_firewall_rule, new_firewall_rule) firewall_rule = self.firewall_db.update_firewall_rule( context, id, firewall_rule_delta) self.update_firewall_rule_postcommit(context, old_firewall_rule, firewall_rule) payload = events.DBEventPayload(context=context, resource_id=id, states=(firewall_rule,)) registry.publish( const.FIREWALL_RULE, events.AFTER_UPDATE, self, payload=payload) return firewall_rule @abc.abstractmethod def update_firewall_rule_precommit(self, context, old_firewall_rule, new_firewall_rule): pass @abc.abstractmethod def update_firewall_rule_postcommit(self, context, old_firewall_rule, new_firewall_rule): pass def insert_rule(self, context, policy_id, rule_info): self.insert_rule_precommit(context, policy_id, rule_info) firewall_policy = self.firewall_db.insert_rule(context, policy_id, rule_info) self.insert_rule_postcommit(context, policy_id, rule_info) payload = events.DBEventPayload(context=context, resource_id=policy_id, states=(firewall_policy,)) registry.publish( const.FIREWALL_POLICY, events.AFTER_UPDATE, self, payload=payload) return firewall_policy @abc.abstractmethod def insert_rule_precommit(self, context, policy_id, rule_info): pass @abc.abstractmethod def insert_rule_postcommit(self, context, policy_id, rule_info): pass def remove_rule(self, context, policy_id, rule_info): self.remove_rule_precommit(context, policy_id, rule_info) firewall_policy = self.firewall_db.remove_rule(context, policy_id, rule_info) self.remove_rule_postcommit(context, policy_id, rule_info) payload = events.DBEventPayload(context=context, resource_id=policy_id, states=(firewall_policy,)) registry.publish( const.FIREWALL_POLICY, events.AFTER_UPDATE, self, payload=payload) return firewall_policy @abc.abstractmethod def remove_rule_precommit(self, context, policy_id, rule_info): pass @abc.abstractmethod def remove_rule_postcommit(self, context, policy_id, rule_info): pass class FirewallDriverDB(FirewallDriverDBMixin): """FirewallDriverDBMixin interface for driver with database. Each firewall backend driver that needs a database persistency should inherit from this driver. It can overload needed methods from the following pre/postcommit methods. Any exception raised during a precommit method will result in not having related records in the databases. """ # Firewall Group def create_firewall_group_precommit(self, context, firewall_group): pass def create_firewall_group_postcommit(self, context, firewall_group): pass def update_firewall_group_precommit(self, context, old_firewall_group, new_firewall_group): pass def update_firewall_group_postcommit(self, context, old_firewall_group, new_firewall_group): pass def delete_firewall_group_precommit(self, context, firewall_group): pass def delete_firewall_group_postcommit(self, context, firewall_group): pass # Firewall Policy def create_firewall_policy_precommit(self, context, firewall_policy): pass def create_firewall_policy_postcommit(self, context, firewall_policy): pass def update_firewall_policy_precommit(self, context, old_firewall_policy, new_firewall_policy): pass def update_firewall_policy_postcommit(self, context, old_firewall_policy, new_firewall_policy): pass def delete_firewall_policy_precommit(self, context, firewall_policy): pass def delete_firewall_policy_postcommit(self, context, firewall_policy): pass # Firewall Rule def create_firewall_rule_precommit(self, context, firewall_rule): pass def create_firewall_rule_postcommit(self, context, firewall_rule): pass def update_firewall_rule_precommit(self, context, old_firewall_rule, new_firewall_rule): pass def update_firewall_rule_postcommit(self, context, old_firewall_rule, new_firewall_rule): pass def delete_firewall_rule_precommit(self, context, firewall_rule): pass def delete_firewall_rule_postcommit(self, context, firewall_rule): pass def insert_rule_precommit(self, context, policy_id, rule_info): pass def insert_rule_postcommit(self, context, policy_id, rule_info): pass def remove_rule_precommit(self, context, policy_id, rule_info): pass def remove_rule_postcommit(self, context, policy_id, rule_info): pass class FirewallDriverRPCMixin(object, metaclass=abc.ABCMeta): """FirewallAgent interface for driver with rpc callback listener. Each firewall backend driver that needs a rpc callback listener should inherit from this driver. """ @abc.abstractmethod def start_rpc_listener(self): pass ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/0000775000175000017500000000000000000000000022356 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/__init__.py0000664000175000017500000000000000000000000024455 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/0000775000175000017500000000000000000000000023637 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/__init__.py0000664000175000017500000000000000000000000025736 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/drivers/0000775000175000017500000000000000000000000025315 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/drivers/__init__.py0000664000175000017500000000000000000000000027414 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/drivers/iptables/0000775000175000017500000000000000000000000027120 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/drivers/iptables/__init__.py0000664000175000017500000000000000000000000031217 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/drivers/iptables/driver.py0000664000175000017500000000504400000000000030770 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited. # 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.services.logapi.drivers import base from neutron.services.logapi.drivers import manager from neutron_lib.callbacks import resources from neutron_lib.services.logapi import constants as log_const from oslo_log import log as logging from oslo_utils import importutils from neutron_fwaas.common import fwaas_constants from neutron_fwaas.services.logapi.common import fwg_callback from neutron_fwaas.services.logapi.common import port_callback from neutron_fwaas.services.logapi import constants as fw_const from neutron_fwaas.services.logapi.rpc import log_server as rpc_server LOG = logging.getLogger(__name__) DRIVER = None SUPPORTED_LOGGING_TYPES = [fw_const.FIREWALL_GROUP] class IptablesLoggingDriver(base.DriverBase): @staticmethod def create(): return IptablesLoggingDriver( name='iptables', vif_types=[], vnic_types=[], supported_logging_types=SUPPORTED_LOGGING_TYPES, requires_rpc=True) def register(): """Register iptables-based logging driver for FWaaS.""" global DRIVER if not DRIVER: DRIVER = IptablesLoggingDriver.create() # Register RPC methods if DRIVER.requires_rpc: rpc_methods = [ {resources.PORT: rpc_server.get_fwg_log_info_for_port}, {log_const.LOG_RESOURCE: rpc_server. get_fwg_log_info_for_log_resources} ] DRIVER.register_rpc_methods(fw_const.FIREWALL_GROUP, rpc_methods) # Trigger fwg validator importutils.import_module('neutron_fwaas.services.logapi.fwg_validate') # Register resource callback handler manager.register( fwaas_constants.FIREWALL_GROUP, fwg_callback.FirewallGroupCallBack) # Register resource callback handler for Neutron ports manager.register(resources.PORT, port_callback.NeutronPortCallBack) LOG.debug('FWaaS L3 Logging driver based iptables registered') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/drivers/iptables/log.py0000664000175000017500000004735500000000000030271 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited. # 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 collections import defaultdict import signal import uuid from neutron.agent.linux import utils from neutron.services.logapi.agent import log_extension as log_ext from neutron_lib import constants from neutron_lib.services.logapi import constants as log_const from oslo_config import cfg from oslo_log import formatters from oslo_log import handlers from oslo_log import log as logging from neutron_fwaas.privileged.netfilter_log import libnetfilter_log as libnflog LOG = logging.getLogger(__name__) UINT64_BITMASK = (1 << 64) - 1 MAX_INTF_NAME_LEN = 14 INTERNAL_DEV_PREFIX = 'qr-' SNAT_INT_DEV_PREFIX = 'sg-' ROUTER_2_FIP_DEV_PREFIX = 'rfp-' IPTABLES_DIRECTION_DEVICE = { constants.INGRESS_DIRECTION: 'i', constants.EGRESS_DIRECTION: 'o' } def setup_logging(): log_file = cfg.CONF.network_log.local_output_log_base if log_file: from logging import handlers as watch_handler log_file_handler = watch_handler.WatchedFileHandler(log_file) log_file_handler.setLevel( logging.DEBUG if cfg.CONF.debug else logging.INFO) LOG.logger.addHandler(log_file_handler) log_file_handler.setFormatter( formatters.ContextFormatter( fmt=cfg.CONF.logging_default_format_string, datefmt=cfg.CONF.log_date_format)) elif cfg.CONF.use_journal: journal_handler = handlers.OSJournalHandler() LOG.logger.addHandler(journal_handler) else: syslog_handler = handlers.OSSysLogHandler() LOG.logger.addHandler(syslog_handler) class LogPrefix(object): """LogPrefix could be used as prefix in NFLOG rules Each of a couple (port_id, event) has its own LogPrefix object """ def __init__(self, port_id, event, project_id): self.id = self._generate_prefix_id() self.port_id = port_id self.action = event # A list of log objects that referenced to this prefix self.log_object_refs = set() self.project_id = project_id def __eq__(self, other): return (self.id == other.id and self.action == other.action and self.port_id == other.port_id) def __hash__(self): return hash(self.id) def _generate_prefix_id(self): return uuid.uuid4().int & UINT64_BITMASK def add_log_obj_ref(self, log_id): self.log_object_refs.add(log_id) def remove_log_obj_ref(self, log_id): self.log_object_refs.discard(log_id) @property def is_empty(self): return not self.log_object_refs class FWGPortLog(object): """A firewall group port log per log_object""" def __init__(self, port_id, log_info): self.port_id = port_id self.log_id = log_info['id'] self.project_id = log_info['project_id'] self.event = log_info['event'] class IptablesLoggingDriver(log_ext.LoggingDriver): SUPPORTED_LOGGING_TYPES = ['firewall_group'] def __init__(self, agent_api): self.agent_api = agent_api self.conf = cfg.CONF self.rate_limit = self.conf.network_log.rate_limit if self.rate_limit: self.burst_limit = self.conf.network_log.burst_limit self.ipt_mgr_list = defaultdict(dict) # A list of fwg port logs that are being logged self.fwg_port_logs = defaultdict(set) # A list of prefixes that are being used in iptables self.prefixes_table = {} self.cleanup_table = defaultdict(set) # Handle NFLOG processing self.nflog_proc_map = {} # A list of unused ports self.unused_port_ids = set() def initialize(self, resource_rpc, **kwargs): self.resource_rpc = resource_rpc setup_logging() self.log_app = libnflog.NFLogApp() self.log_app.register_packet_handler(self.log_packet) self.log_app.start() def log_packet(self, ev): prefix = ev['prefix'] pkt = ev['msg'] prefix_entry = self._get_prefix_by_id(prefix) if prefix_entry: logs_id = [str(id) for id in prefix_entry.log_object_refs] LOG.info("action=%s, project_id=%s, log_resource_ids=%s, port=%s, " "pkt=%s", prefix_entry.action, prefix_entry.project_id, logs_id, prefix_entry.port_id, pkt) else: LOG.warning("Unknown cookie packet_in pkt=%s", pkt) return 0 def _get_prefix(self, port_id, action): if port_id in self.prefixes_table: for prefix in self.prefixes_table[port_id]: if prefix.action == action: return prefix return None def _get_prefix_by_id(self, prefix_id): for port, prefixes in self.prefixes_table.items(): for prefix in prefixes: if str(prefix.id) == str(prefix_id): return prefix return None def _add_to_cleanup(self, port_id, prefix_id): if port_id not in self.cleanup_table: self.cleanup_table[port_id] = set() self.cleanup_table[port_id].add(prefix_id) def _add_to_prefixes_table(self, port_id, prefix): if port_id not in self.prefixes_table: self.prefixes_table[port_id] = [] self.prefixes_table[port_id].append(prefix) def _cleanup_nflog_process(self, router_info): LOG.debug("Delete router_info %s", router_info) if router_info in self.nflog_proc_map: pid = self.nflog_proc_map[router_info] try: # A process started by a root helper will be running as # root and need to be killed via the same helper. LOG.debug('Trying to kill NFLOG process %d', pid) utils.kill_process(pid, signal.SIGKILL, run_as_root=True) del self.nflog_proc_map[router_info] except Exception: LOG.exception( 'An error occurred while killing process %d', pid) def _cleanup_prefix_by_router(self, router_id): ipt_mgr_per_port = set() for port_id in self.ipt_mgr_list[router_id]: ipt_mgr = self.ipt_mgr_list[router_id][port_id] ipt_mgr_per_port.add(ipt_mgr) # Cleanup prefix if port_id in self.prefixes_table: for prefix in self.prefixes_table[port_id]: self._add_to_cleanup(port_id, prefix.id) del self.prefixes_table[port_id] self.unused_port_ids.add(port_id) return ipt_mgr_per_port def _cleanup_unused_ipt_mgrs(self): need_cleanup = set() for port_id in self.unused_port_ids: for router_id in self.ipt_mgr_list: if port_id in self.ipt_mgr_list[router_id]: del self.ipt_mgr_list[router_id][port_id] if not self.ipt_mgr_list[router_id]: need_cleanup.add(router_id) for router_id in need_cleanup: del self.ipt_mgr_list[router_id] self.unused_port_ids.clear() def start_logging(self, context, **kwargs): LOG.debug("Start logging: %s", str(kwargs)) for resource_type in self.SUPPORTED_LOGGING_TYPES: router_info = kwargs.get('router_info') if router_info: # Handle router updated or L3 agent restart router_id = router_info.router_id internal_ports = router_info.internal_ports self._create_firewall_group_log(context, resource_type, ports=internal_ports, router_id=router_id) # Start libnetfilter_log after router starting up pid = libnflog.run_nflog(router_info.ns_name) LOG.debug("NFLOG process ID %s for router %s has started", pid, router_info.router_id) self.nflog_proc_map[router_id] = pid else: # Handle the log request self._create_firewall_group_log(context, resource_type, **kwargs) def stop_logging(self, context, **kwargs): LOG.debug("Stop logging: %s", str(kwargs)) # Delete router router_info = kwargs.get('router_info') if router_info: self._cleanup_nflog_process(router_info) if kwargs.get('log_resources'): # Handle incoming log request self._delete_firewall_group_log(context, **kwargs) def _create_firewall_group_log(self, context, resource_type, **kwargs): ports = kwargs.get('ports') log_resources = kwargs.get('log_resources') applied_ipt_mgrs = set() logs_info = [] port_ids = [] # Get log objects from database via RPC if ports: port_ids = [port['id'] for port in ports] logs_info = self.resource_rpc. \ get_sg_log_info_for_port(context, resource_type, port_id=port_ids) elif log_resources: logs_info = self.resource_rpc.\ get_sg_log_info_for_log_resources(context, resource_type, log_resources=log_resources) # Handle logs_info for log_info in logs_info: log_id = log_info['id'] old_fwg_port_logs = self.fwg_port_logs.get(log_id, []) new_ports_log = log_info.get('ports_log') self.fwg_port_logs[log_id] = set() for port in new_ports_log: self._add_fwg_port_log(log_id, port, log_info) for port in old_fwg_port_logs: if port.port_id not in new_ports_log: # Remove port not bound by log_id self._cleanup_prefixes_table(port.port_id, log_id) for fwg_port_log in self.fwg_port_logs[log_id]: self._setup_chains(applied_ipt_mgrs, fwg_port_log) router_id = kwargs.get("router_id") if router_id: if not port_ids: ipt_mgrs = self._cleanup_prefix_by_router(router_id) applied_ipt_mgrs.update(ipt_mgrs) for port_id in port_ids: try: ipt_mgr = self.ipt_mgr_list[router_id][port_id] applied_ipt_mgrs.add(ipt_mgr) except KeyError: pass # Clean up NFLOG rules self._cleanup_nflog_rules(applied_ipt_mgrs) # Apply NFLOG rules into iptables managers for ipt_mgr in applied_ipt_mgrs: LOG.debug('Apply NFLOG rules in namespace %s', ipt_mgr.namespace) ipt_mgr.defer_apply_off() # Clean up unused iptables managers from ports self._cleanup_unused_ipt_mgrs() def _cleanup_prefixes_table(self, port_id, log_id): # Each a port has at most 2 prefix for index in [1, 0]: try: prefix = self.prefixes_table[port_id][index] prefix.remove_log_obj_ref(log_id) if prefix.is_empty: self._add_to_cleanup(port_id, prefix.id) self.prefixes_table[port_id].remove(prefix) except Exception: pass if port_id in self.prefixes_table: if not self.prefixes_table[port_id]: del self.prefixes_table[port_id] self.unused_port_ids.add(port_id) def _cleanup_nflog_rules(self, applied_ipt_mgrs): for port_id, prefix_ids in self.cleanup_table.items(): ipt_mgr = self._get_ipt_mgr_by_port(port_id) for prefix_id in prefix_ids: self._clear_rules_from_tag_v4v6(ipt_mgr, tag=prefix_id) applied_ipt_mgrs.add(ipt_mgr) self.cleanup_table.clear() def _delete_firewall_group_log(self, context, **kwargs): log_resources = kwargs.get('log_resources') applied_ipt_mgrs = set() for log_resource in log_resources: log_id = log_resource.get('id') fwg_port_logs = self.fwg_port_logs[log_id] for port in fwg_port_logs: self._cleanup_prefixes_table(port.port_id, log_id) del self.fwg_port_logs[log_id] # Clean NFLOG rules: self._cleanup_nflog_rules(applied_ipt_mgrs) # Apply NFLOG rules into iptables managers for ipt_mgr in applied_ipt_mgrs: ipt_mgr.defer_apply_off() # Clean up unused iptables managers self._cleanup_unused_ipt_mgrs() def _get_if_prefix(self, agent_mode, router): """Get the if prefix from router""" if not router.router.get('distributed'): return INTERNAL_DEV_PREFIX if agent_mode == 'dvr_snat': return SNAT_INT_DEV_PREFIX if router.rtr_fip_connect: return ROUTER_2_FIP_DEV_PREFIX def _get_intf_name(self, port_id): agent_mode = self.conf.agent_mode router = self.agent_api.get_router_hosting_port(port_id) if_prefix = self._get_if_prefix(agent_mode, router) return (if_prefix + port_id)[:constants.LINUX_DEV_LEN] def _get_ipt_mgr_by_port(self, port_id): router = self.agent_api.get_router_hosting_port(port_id) if router: try: ipt_mgr = self.ipt_mgr_list[router.router_id][port_id] return ipt_mgr except KeyError: pass ipt_mgr = router.iptables_manager self.ipt_mgr_list[router.router_id][port_id] = ipt_mgr return ipt_mgr for router_id in self.ipt_mgr_list: if port_id in self.ipt_mgr_list[router_id]: return self.ipt_mgr_list[router_id][port_id] def _setup_chains(self, applied_ipt_mgrs, fwg_port_log): # Add NFLOG rules by log event event = fwg_port_log.event if event in [log_const.ACCEPT_EVENT, log_const.ALL_EVENT]: self._add_nflog_rules_accepted(applied_ipt_mgrs, fwg_port_log) if event in [log_const.DROP_EVENT, log_const.ALL_EVENT]: self._add_log_rules_dropped(applied_ipt_mgrs, fwg_port_log) def _add_nflog_rules_accepted(self, applied_ipt_mgrs, fwg_port_log): """Add NFLOG rules to the accepted chain into iptables""" action = log_const.ACCEPT_EVENT port_id = fwg_port_log.port_id prefix = self._get_prefix(port_id, action) if not prefix: # Generate a new prefix from port and action project_id = fwg_port_log.project_id prefix = LogPrefix(port_id, action, project_id) self._add_to_prefixes_table(port_id, prefix) # Get iptables manager from router port ipt_mgr = self._get_ipt_mgr_by_port(port_id) if ipt_mgr: applied_ipt_mgrs.add(ipt_mgr) device = self._get_intf_name(port_id) # Add the NFLOG rules to the dropped chain into iptables ipv4_rules, ipv6_rules = \ self._generate_nflog_rules_v4v6(device, prefix=prefix.id) self._add_rules_to_chain_v4v6(ipt_mgr, 'accepted', ipv4_rules, ipv6_rules, wrap=True, top=True, tag=prefix.id) prefix.add_log_obj_ref(fwg_port_log.log_id) def _add_log_rules_dropped(self, applied_ipt_mgrs, fwg_port_log): """Add NFLOG rules to the dropped chain into iptables""" action = log_const.DROP_EVENT port_id = fwg_port_log.port_id prefix = self._get_prefix(port_id, action) if not prefix: # Generate a new prefix from port and action project_id = fwg_port_log.project_id prefix = LogPrefix(port_id, action, project_id) self._add_to_prefixes_table(port_id, prefix) device = self._get_intf_name(port_id) # Get iptables manager from router port ipt_mgr = self._get_ipt_mgr_by_port(port_id) if ipt_mgr: applied_ipt_mgrs.add(ipt_mgr) # Add the NFLOG rules to the dropped chain into iptables ipv4_rules, ipv6_rules = \ self._generate_nflog_rules_v4v6(device, prefix=prefix.id) self._add_rules_to_chain_v4v6(ipt_mgr, 'dropped', ipv4_rules, ipv6_rules, wrap=True, top=True, tag=prefix.id) # Add the NFLOG rules to the rejected chain in iptables self._add_rules_to_chain_v4v6(ipt_mgr, 'rejected', ipv4_rules, ipv6_rules, wrap=True, top=True, tag=prefix.id) prefix.add_log_obj_ref(fwg_port_log.log_id) def _add_rules_to_chain_v4v6(self, ipt_mgr, chain_name, v4_rules, v6_rules, wrap=False, comment=None, top=False, tag=None): """Add rules to filter table in iptables and ip6tables""" for rule in v4_rules: ipt_mgr.ipv4['filter'].add_rule(chain_name, rule, wrap=wrap, comment=comment, top=top, tag=tag) for rule in v6_rules: ipt_mgr.ipv6['filter'].add_rule(chain_name, rule, wrap=wrap, comment=comment, top=top, tag=tag) def _add_fwg_port_log(self, log_id, port_id, log_info): self.fwg_port_logs[log_id].add(FWGPortLog(port_id, log_info)) # Add log ID into the corresponding LogPrefix object if log_info['event'] == log_const.ALL_EVENT: events = [log_const.ACCEPT_EVENT, log_const.DROP_EVENT] else: events = [log_info['event']] for event in events: prefix = self._get_prefix(port_id, event) if prefix: prefix.add_log_obj_ref(log_id) def _generate_nflog_rules_v4v6(self, device, prefix): iptables_rules = [] added_rules = set() for direction in constants.VALID_DIRECTIONS: args = self._generate_iptables_args(direction, device, prefix) rule = ' '.join(args) if rule in added_rules: # Since these rules are already added to iptables, # so we ignore them here continue added_rules.add(rule) iptables_rules.append(rule) LOG.debug("iptables-nflog-rules %r", iptables_rules) return iptables_rules, iptables_rules def _generate_iptables_args(self, direction, device, prefix=None): direction_config = ['-%s %s' % (IPTABLES_DIRECTION_DEVICE[direction], device)] match_rule = [] if self.rate_limit: match_rule += ['-m', 'limit', '--limit', '%s/s' % self.rate_limit] if self.burst_limit: match_rule += ['--limit-burst %s' % self.burst_limit] target = ['-j', 'NFLOG'] if prefix: target += ['--nflog-prefix', '%s' % prefix] args = direction_config + match_rule + target return args def _clear_rules_from_tag_v4v6(self, ipt_mgt, tag): """Remove rules from filter table in iptables and ip6tables""" ipt_mgt.ipv4['filter'].clear_rules_by_tag(tag) ipt_mgt.ipv6['filter'].clear_rules_by_tag(tag) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.309351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/l3/0000775000175000017500000000000000000000000024155 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/l3/__init__.py0000664000175000017500000000000000000000000026254 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/agents/l3/fwg_log.py0000664000175000017500000000313500000000000026155 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.services.logapi.agent.l3 import base from neutron.services.logapi.agent import log_extension as log_ext from neutron.services.logapi.rpc import agent as agent_rpc from neutron_lib.agent import l3_extension # TODO(annp) move to neutron-lib FIREWALL_LOG_DRIVER_NAME = 'fwaas_v2_log' class FWaaSL3LoggingExtension(base.L3LoggingExtensionBase, l3_extension.L3AgentExtension): def initialize(self, connection, driver_type): """Initialize L3 logging agent extension""" fw_log_cls = self._load_driver_cls( log_ext.LOGGING_DRIVERS_NAMESPACE, FIREWALL_LOG_DRIVER_NAME) self.log_driver = fw_log_cls(self.agent_api) self.resource_rpc = agent_rpc.LoggingApiStub() self._register_rpc_consumers() self.log_driver.initialize(self.resource_rpc) def update_network(self, context, data): # TODO(zhouhenglc) remove at base.L3LoggingExtensionBase support # update_network pass ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.313351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/common/0000775000175000017500000000000000000000000023646 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/common/__init__.py0000664000175000017500000000000000000000000025745 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/common/fwg_callback.py0000664000175000017500000000460600000000000026625 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects import ports as port_objects from neutron.services.logapi.drivers import manager from neutron_lib.callbacks import events from neutron_lib import constants as nl_const from neutron_lib.services.logapi import constants as log_const from neutron_fwaas.services.logapi.common import log_db_api class FirewallGroupCallBack(manager.ResourceCallBackBase): def handle_event(self, resource, event, trigger, **kwargs): payload = kwargs.get('payload') context = payload.context ports_delta = [] if event == events.AFTER_CREATE: # Update log when a new firewall group is created with ports ports_delta = payload.latest_state['ports'] elif event == events.AFTER_UPDATE: old_ports = payload.states[0]['ports'] new_ports = payload.states[1]['ports'] # Check whether port is updated from firewall group or not ports_delta = \ set(new_ports).symmetric_difference(set(old_ports)) if self.need_to_notify(context, ports_delta): self.trigger_logging(context, payload.resource_id, ports_delta) def trigger_logging(self, context, fwg_id, ports_delta): log_resources = log_db_api.get_logs_for_fwg( context, fwg_id, ports_delta) if log_resources: self.resource_push_api( log_const.RESOURCE_UPDATE, context, log_resources) def need_to_notify(self, context, ports): notify = False for port_id in ports: port = port_objects.Port.get_object(context, id=port_id) device_owner = port.get('device_owner', '') if device_owner in nl_const.ROUTER_INTERFACE_OWNERS: notify = True break return notify ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/common/log_db_api.py0000664000175000017500000001717400000000000026311 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects.logapi import logging_resource as log_object from neutron.objects import ports as port_objects from neutron_lib import constants as nl_const from neutron_lib.plugins import directory from neutron_fwaas.common import fwaas_constants from neutron_fwaas.services.logapi import constants fw_plugin_db = None # TODO(longkb): We will support L2 port. Currently, this method returns only # router ports. def _get_ports_being_logged(context, log_obj): """Return a list of ports being logged with a given log_obj""" target_id = log_obj['target_id'] resource_id = log_obj['resource_id'] # If 'target_id' (port_id) is specified in a log_obj if target_id: fwg_id = fw_plugin_db.get_fwg_attached_to_port(context, target_id) if fwg_id: port_ids = [target_id] else: port_ids = [] # If 'resource_id' (fwg_id) is specified in a log_obj elif resource_id: port_ids = \ fw_plugin_db.get_ports_in_firewall_group(context, resource_id) # Both 'resource_id' and 'target_id' aren't specified in a log_resource else: tenant_id = log_obj['project_id'] port_ids = fw_plugin_db.get_fwg_ports_in_tenant(context, tenant_id) filtered_port_ids = [] for port_id in port_ids: port = port_objects.Port.get_object(context, id=port_id) device_owner = port.get('device_owner', '') # TODO(longkb): L2 ports will be supported in the future # Check whether a port is router port or not. if device_owner in nl_const.ROUTER_INTERFACE_OWNERS: # Checking port status if port.get('status') == nl_const.PORT_STATUS_ACTIVE: # Check whether a port is attached to firewall group or not fwg = fw_plugin_db.get_fwg_attached_to_port(context, port_id) if fwg: filtered_port_ids.append(port_id) return filtered_port_ids def _make_log_info_dict(log_obj, port_ids): log_info = { 'id': log_obj['id'], 'ports_log': port_ids, 'event': log_obj['event'], 'project_id': log_obj['project_id'] } return log_info def get_logs_for_port(context, port_id): """Return a list of log_resources bound to a given port_id""" global fw_plugin_db if not fw_plugin_db: fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) # NOTE(longkb): check whether fw plugin was loaded or not. if not fw_plugin: return [] fw_plugin_db = fw_plugin.driver.firewall_db logs_bounded = [] port = port_objects.Port.get_object(context, id=port_id) if not port: return logs_bounded # Ignore if a given port_id is not belong to router port device_owner = port.get('device_owner', '') if device_owner not in nl_const.ROUTER_INTERFACE_OWNERS: return logs_bounded # Ignore if a given port does not attach to any fwg fwg_id = fw_plugin_db.get_fwg_attached_to_port(context, port_id) if not fwg_id: return logs_bounded project_id = port['project_id'] log_objs = log_object.Log.get_objects( context, project_id=project_id, resource_type=constants.FIREWALL_GROUP, enabled=True) for log_obj in log_objs: if log_obj.resource_id == fwg_id: logs_bounded.append(log_obj) elif log_obj.target_id == port['id']: logs_bounded.append(log_obj) elif not log_obj.target_id and not log_obj.resource_id: logs_bounded.append(log_obj) return logs_bounded def get_logs_for_fwg(context, fwg_id, ports_delta): """Return a list of log_resources bound to a firewall group""" global fw_plugin_db if not fw_plugin_db: fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) # NOTE(longkb): check whether fw plugin was loaded or not. if not fw_plugin: return [] fw_plugin_db = fw_plugin.driver.firewall_db project_id = context.tenant_id log_objs = log_object.Log.get_objects( context, project_id=project_id, resource_type=constants.FIREWALL_GROUP, enabled=True) log_resources = [] for log_obj in log_objs: if log_obj.resource_id == fwg_id: log_resources.append(log_obj) elif log_obj.target_id in ports_delta: log_resources.append(log_obj) elif not log_obj.resource_id and not log_obj.target_id: log_resources.append(log_obj) return log_resources def get_fwg_log_info_for_port(context, port_ids): """Return a list of firewall group log info for a given port The list has format as below: [ { 'event': 'ALL', 'id': '733e0499-e69e-4106-a84a-635fbc5fbbc0', 'project_id': '46f70361-ba71-4bd0-9769-3573fd227c4b', 'ports_log': [ port1_id, port2_id, ] }, ] :param context: current running context information :param port_ids: list of ports which needed to get firewall group log info """ global fw_plugin_db if not fw_plugin_db: fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) # NOTE(longkb): check whether fw plugin was loaded or not. if not fw_plugin: return [] fw_plugin_db = fw_plugin.driver.firewall_db logs_info = [] log_bounds = set() for port_id in port_ids: log_objs = get_logs_for_port(context, port_id) if log_objs: log_bounds |= set(log_objs) if log_bounds: for log_resource in log_bounds: port_ids = _get_ports_being_logged(context, log_resource) log_info = _make_log_info_dict(log_resource, port_ids) logs_info.append(log_info) return logs_info def get_fwg_log_info_for_log_resources(context, log_resources): """Return a list of firewall group log info for list of log_resources The list has format as below: [ { 'event': 'ALL', 'id': '733e0499-e69e-4106-a84a-635fbc5fbbc0', 'project_id': '46f70361-ba71-4bd0-9769-3573fd227c4b', 'ports_log': [ port1_id, port2_id, ] }, ] :param context: current running context information :param log_resources: list of log_resources, which needed to get firewall groups log info """ global fw_plugin_db if not fw_plugin_db: fw_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) # NOTE(longkb): check whether fw plugin was loaded or not. if not fw_plugin: return [] fw_plugin_db = fw_plugin.driver.firewall_db logs_info = [] for log_resource in log_resources: ports_id = _get_ports_being_logged(context, log_resource) log_info = _make_log_info_dict(log_resource, ports_id) logs_info.append(log_info) return logs_info ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/common/port_callback.py0000664000175000017500000000313100000000000027016 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.services.logapi.drivers import manager from neutron_lib.callbacks import events from neutron_lib import constants as nl_const from neutron_lib.services.logapi import constants as log_const from neutron_fwaas.services.logapi.common import log_db_api class NeutronPortCallBack(manager.ResourceCallBackBase): def handle_event(self, resource, event, trigger, payload): if event == events.AFTER_UPDATE: context = payload.context original_port = payload.states[0] port = payload.states[1] if port['device_owner'] in nl_const.ROUTER_INTERFACE_OWNERS: if original_port['status'] != port['status']: self.trigger_logging(context, port) def trigger_logging(self, context, port): log_resources = log_db_api.get_logs_for_port(context, port['id']) if log_resources: self.resource_push_api( log_const.RESOURCE_UPDATE, context, log_resources) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/constants.py0000664000175000017500000000145200000000000024746 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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. # Firewall group logging resource type FIREWALL_GROUP = 'firewall_group' # Target logging resource type TARGET_RESOURCE = 'port which is associated with the firewall group' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/exceptions.py0000664000175000017500000000237600000000000025121 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron._i18n import _ from neutron_lib import exceptions as n_exc # TODO(annp or longkb): move to neutron-lib class FWGIsNotReadyForLogging(n_exc.InvalidInput): message = _("Firewall group %(fwg_id)s is not ready for logging " "because of %(fwg_status)s status.") class TargetResourceNotAssociated(n_exc.InvalidInput): message = _("Target resource %(target_id)s is not associated with " "any firewall group.") class PortIsNotReadyForLogging(n_exc.InvalidInput): message = _("Target resource %(target_id)s is not ready for logging " "because of %(port_status)s status.") ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/fwg_validate.py0000664000175000017500000001170700000000000025372 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects import ports from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.common import validators from neutron_lib import constants as nl_const from neutron_lib.plugins import directory from sqlalchemy.orm import exc as orm_exc from neutron_fwaas.common import fwaas_constants from neutron_fwaas.services.logapi import constants as log_const from neutron_fwaas.services.logapi import exceptions as fwg_log_exc fwg_plugin = None def _check_fwg(context, fwg_id): try: fwg = fwg_plugin.get_firewall_group(context, id=fwg_id) except orm_exc.NoResultFound: raise log_exc.ResourceNotFound(resource_id=fwg_id) if fwg['status'] != nl_const.ACTIVE: raise fwg_log_exc.FWGIsNotReadyForLogging( fwg_id=fwg_id, fwg_status=fwg['status']) def _check_fwg_port(context, port_id): # Checking port exists port = ports.Port.get_object(context, id=port_id) if not port: raise log_exc.TargetResourceNotFound(target_id=port_id) device_owner = port.get('device_owner', '') # Checking supported firewall group logging for vm port if device_owner.startswith(nl_const.DEVICE_OWNER_COMPUTE_PREFIX): if not validators.validate_log_type_for_port( log_const.FIREWALL_GROUP, port): raise log_exc.LoggingTypeNotSupported( log_type=log_const.FIREWALL_GROUP, port_id=port_id) # Checking supported firewall group for router interface, DVR interface, # and HA replicated interface elif device_owner not in nl_const.ROUTER_INTERFACE_OWNERS: raise log_exc.LoggingTypeNotSupported( log_type=log_const.FIREWALL_GROUP, port_id=port_id) # Checking port status port_status = port.get('status') if port_status != nl_const.PORT_STATUS_ACTIVE: raise fwg_log_exc.PortIsNotReadyForLogging(target_id=port_id, port_status=port_status) # Checking whether router port or vm port binding with any firewall group fwg_id = fwg_plugin.driver.firewall_db.get_fwg_attached_to_port( context, port_id=port_id) if not fwg_id: raise fwg_log_exc.TargetResourceNotAssociated(target_id=port_id) fwg = fwg_plugin.get_firewall_group(context, id=fwg_id) if fwg['status'] != nl_const.ACTIVE: raise fwg_log_exc.FWGIsNotReadyForLogging(fwg_id=fwg_id, fwg_status=fwg['status']) def _check_target_resource_bound_fwg(context, fwg_id, target_id): ports = fwg_plugin.driver.firewall_db.get_ports_in_firewall_group( context=context, firewall_group_id=fwg_id) if target_id not in ports: raise log_exc.InvalidResourceConstraint( resource=log_const.FIREWALL_GROUP, resource_id=fwg_id, target_resource=log_const.TARGET_RESOURCE, target_id=target_id) @validators.ResourceValidateRequest.register(log_const.FIREWALL_GROUP) def validate_firewall_group_request(context, log_data): """Validate a log request This method validates log request is satisfied or not. A ResourceNotFound will be raised if resource_id in log_data not exists or a TargetResourceNotFound will be raised if target_id in log_data not exists. Beside, FWGIsNotReadyForLogging will be raised in the case of queried firewall group is not in ACTIVE state. PortIsNotReadyForLogging exception will be raised if port is not in ACTIVE status. Besides, TargetResourceNotAssociated exception will be raised if a given port does not have any firewall group attach to. This method will also raise a LoggingTypeNotSupported, if there is no log_driver supporting for resource_type in log_data. In addition, if log_data specify both resource_id and target_id. A InvalidResourceConstraint will be raised if there is no constraint between resource_id and target_id. """ global fwg_plugin if not fwg_plugin: fwg_plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) resource_id = log_data.get('resource_id') target_id = log_data.get('target_id') if resource_id and target_id: _check_target_resource_bound_fwg(context, resource_id, target_id) if resource_id: _check_fwg(context, resource_id) if target_id: _check_fwg_port(context, target_id) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.313351 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/rpc/0000775000175000017500000000000000000000000023142 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/rpc/__init__.py0000664000175000017500000000000000000000000025241 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/services/logapi/rpc/log_server.py0000664000175000017500000000214600000000000025666 0ustar00zuulzuul00000000000000# Copyright (C) 2018 Fujitsu Limited # 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_fwaas.services.logapi.common import log_db_api # Use this when register log driver with # "register_rpc_methods" function def get_fwg_log_info_for_port(context, port_id): return log_db_api.get_fwg_log_info_for_port(context, port_id) # Use this when register log driver with # "register_rpc_methods" function def get_fwg_log_info_for_log_resources(context, log_resources): return log_db_api.get_fwg_log_info_for_log_resources( context, log_resources) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.313351 neutron-fwaas-20.0.0/neutron_fwaas/tests/0000775000175000017500000000000000000000000020422 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/__init__.py0000664000175000017500000000000000000000000022521 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/base.py0000664000175000017500000000133400000000000021707 0ustar00zuulzuul00000000000000# Copyright 2014 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. # from neutron.tests import base as n_base class BaseTestCase(n_base.BaseTestCase): pass ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.313351 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/0000775000175000017500000000000000000000000022062 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/README0000664000175000017500000000020400000000000022736 0ustar00zuulzuul00000000000000The files in this directory are intended for use by the Neutron infra jobs that run the various functional test suites in the gate. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/filters.template0000664000175000017500000000137400000000000025274 0ustar00zuulzuul00000000000000# neutron-rootwrap command filters to support functional testing. It # is NOT intended to be used outside of a test environment. # # This file should be owned by (and only-writeable by) the root user [Filters] # '$BASE_PATH' is intended to be replaced with the expected tox path # (e.g. /opt/stack/new/neutron/.tox/dsvm-functional) by the neutron # functional jenkins job. This ensures that tests can kill the # processes that they launch with their containing tox environment's # python. kill_tox_python: KillFilter, root, $BASE_PATH/bin/python, -9 # enable ping from namespace ping_filter: CommandFilter, ping, root # enable curl from namespace curl_filter: CommandFilter, curl, root tee_filter: CommandFilter, tee, root tee_kill: KillFilter, root, tee, -9 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/functional-testing.filters0000664000175000017500000000054700000000000027277 0ustar00zuulzuul00000000000000# neutron-rootwrap command filters to support functional testing. It # is NOT intended to be used outside of a test environment. # # This file should be owned by (and only-writeable by) the root user [Filters] # enable ping from namespace ping_filter: CommandFilter, ping, root ping6_filter: CommandFilter, ping6, root ping_kill: KillFilter, root, ping, -2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/gate_hook.sh0000664000175000017500000000116300000000000024357 0ustar00zuulzuul00000000000000#!/bin/bash set -ex VENV=${1:-"dsvm-functional"} GATE_DEST=$BASE/new FWAAS_PATH=$GATE_DEST/neutron-fwaas DEVSTACK_PATH=$GATE_DEST/devstack case $VENV in "dsvm-functional"|"dsvm-fullstack") # The following need to be set before sourcing # configure_for_fwaas_func_testing. GATE_STACK_USER=stack PROJECT_NAME=neutron-fwaas IS_GATE=True source $FWAAS_PATH/tools/configure_for_fwaas_func_testing.sh configure_host_for_func_testing if is_ubuntu || is_suse; then install_package libnetfilter-log1 elif is_fedora; then install_package libnetfilter-log fi ;; esac ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/gate_hook_tempest.sh0000775000175000017500000000204600000000000026124 0ustar00zuulzuul00000000000000#!/bin/bash set -ex FWAAS_VERSION=$1 GATE_DEST=$BASE/new GATE_HOOKS=$GATE_DEST/neutron-fwaas/neutron_fwaas/tests/contrib/hooks DEVSTACK_PATH=$GATE_DEST/devstack LOCAL_CONF=$DEVSTACK_PATH/late-local.conf DSCONF=/tmp/devstack-tools/bin/dsconf # Install devstack-tools used to produce local.conf; we can't rely on # test-requirements.txt because the gate hook is triggered before neutron-fwaas # is installed sudo -H pip install virtualenv virtualenv /tmp/devstack-tools /tmp/devstack-tools/bin/pip install -U devstack-tools==0.4.0 # Inject config from hook into localrc function load_rc_hook { local hook="$1" local tmpfile local config tmpfile=$(tempfile) config=$(cat $GATE_HOOKS/$hook) echo "[[local|localrc]]" > $tmpfile $DSCONF setlc_raw $tmpfile "$config" $DSCONF merge_lc $LOCAL_CONF $tmpfile rm -f $tmpfile } LOCAL_CONF=$DEVSTACK_PATH/local.conf load_rc_hook api_extensions-base load_rc_hook api_extensions-${FWAAS_VERSION} export DEVSTACK_LOCALCONF=$(cat $LOCAL_CONF) $BASE/new/devstack-gate/devstack-vm-gate.sh ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.313351 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/hooks/0000775000175000017500000000000000000000000023205 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/hooks/api_extensions-base0000664000175000017500000000045000000000000027067 0ustar00zuulzuul00000000000000NETWORK_API_EXTENSIONS=agent,binding,dhcp_agent_scheduler,external-net,ext-gw-mode,extra_dhcp_opts,quotas,router,security-group,subnet_allocation,network-ip-availability,auto-allocated-topology,timestamp_core,tag,service-type,rbac-policies,standard-attr-description,pagination,sorting,project-id ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/hooks/api_extensions-legacy0000664000175000017500000000006400000000000027422 0ustar00zuulzuul00000000000000NETWORK_API_EXTENSIONS+=,fwaas,fwaasrouterinsertion ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/hooks/api_extensions-v10000664000175000017500000000006400000000000026504 0ustar00zuulzuul00000000000000NETWORK_API_EXTENSIONS+=,fwaas,fwaasrouterinsertion ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/hooks/api_extensions-v20000664000175000017500000000012200000000000026500 0ustar00zuulzuul00000000000000NETWORK_API_EXTENSIONS+=,fwaas_v2 NETWORK_API_EXTENSIONS+=,standard-attr-fwaas-v2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/hooks/iptables_verify0000664000175000017500000000011500000000000026314 0ustar00zuulzuul00000000000000[[post-config|/etc/neutron/neutron.conf]] [AGENT] debug_iptables_rules=True ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/contrib/post_test_hook.sh0000664000175000017500000000234200000000000025463 0ustar00zuulzuul00000000000000#!/usr/bin/env bash set -xe FWAAS_DIR="$BASE/new/neutron-fwaas" NEUTRON_DIR="$BASE/new/neutron" TEMPEST_DIR="$BASE/new/tempest" SCRIPTS_DIR="/usr/os-testr-env/bin" venv=${1:-"dsvm-functional"} function generate_testr_results { # Give job user rights to access tox logs sudo -H -u $owner chmod o+rw . sudo -H -u $owner chmod o+rw -R .stestr if [ -f ".stestr/0" ] ; then .tox/$venv/bin/subunit-1to2 < .stestr/0 > ./stestr.subunit $SCRIPTS_DIR/subunit2html ./stestr.subunit testr_results.html gzip -9 ./stestr.subunit gzip -9 ./testr_results.html sudo mv ./*.gz /opt/stack/logs/ fi } function dsvm_functional_prep_func { : } if [[ "$venv" == dsvm-functional* ]] then owner=stack sudo_env= # Set owner permissions according to job's requirements. cd $FWAAS_DIR sudo chown -R $owner:stack $FWAAS_DIR sudo chown -R $owner:stack $NEUTRON_DIR # Prep the environment according to job's requirements. $prep_func # Run tests echo "Running neutron-fwaas $venv test suite" set +e sudo -H -u $owner $sudo_env tox -e $venv testr_exit_code=$? set -e # Collect and parse results generate_testr_results exit $testr_exit_code fi ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/0000775000175000017500000000000000000000000022412 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/README0000664000175000017500000000012400000000000023267 0ustar00zuulzuul00000000000000Please see neutron/TESTING.rst for more information about what Fullstack tests are. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/__init__.py0000664000175000017500000000115400000000000024524 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.common import eventlet_utils eventlet_utils.monkey_patch() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/base.py0000664000175000017500000000506000000000000023677 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import os from neutron_lib.tests import tools from oslo_config import cfg from neutron.tests import base as tests_base from neutron.tests.fullstack.resources import client as client_resource from neutron.tests.unit import testlib_api # This is the directory from which infra fetches log files for fullstack tests DEFAULT_LOG_DIR = os.path.join('/opt/stack/logs/neutron-fwaas/', 'dsvm-fullstack-logs') class BaseFullStackTestCase(testlib_api.MySQLTestCaseMixin, testlib_api.SqlTestCase): """Base test class for full-stack tests.""" BUILD_WITH_MIGRATIONS = True def setUp(self, environment): super(BaseFullStackTestCase, self).setUp() tests_base.setup_test_logging( cfg.CONF, DEFAULT_LOG_DIR, '%s.txt' % self.get_name()) # NOTE(zzzeek): the opportunistic DB fixtures have built for # us a per-test (or per-process) database. Set the URL of this # database in CONF as the full stack tests need to actually run a # neutron server against this database. _orig_db_url = cfg.CONF.database.connection cfg.CONF.set_override( 'connection', str(self.engine.url), group='database') self.addCleanup( cfg.CONF.set_override, "connection", _orig_db_url, group="database" ) # NOTE(ihrachys): seed should be reset before environment fixture below # since the latter starts services that may rely on generated port # numbers tools.reset_random_seed() self.environment = environment self.environment.test_name = self.get_name() self.useFixture(self.environment) self.client = self.environment.neutron_server.client self.safe_client = self.useFixture( client_resource.ClientFixture(self.client)) def get_name(self): class_name, test_name = self.id().split(".")[-2:] return "%s.%s" % (class_name, test_name) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/0000775000175000017500000000000000000000000024424 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/__init__.py0000664000175000017500000000000000000000000026523 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/client.py0000664000175000017500000002157000000000000026261 0ustar00zuulzuul00000000000000# Copyright (c) 2015 Thales Services SAS # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 functools import netaddr import fixtures from neutron_lib import constants from neutronclient.common import exceptions from neutron.common import utils from neutron.extensions import portbindings def _safe_method(f): @functools.wraps(f) def delete(*args, **kwargs): try: return f(*args, **kwargs) except exceptions.NotFound: pass return delete class ClientFixture(fixtures.Fixture): """Manage and cleanup neutron resources.""" def __init__(self, client): super(ClientFixture, self).__init__() self.client = client def _create_resource(self, resource_type, spec): create = getattr(self.client, 'create_%s' % resource_type) delete = getattr(self.client, 'delete_%s' % resource_type) body = {resource_type: spec} resp = create(body=body) data = resp[resource_type] self.addCleanup(_safe_method(delete), data['id']) return data def create_router(self, tenant_id, name=None, ha=False, external_network=None): resource_type = 'router' name = name or utils.get_rand_name(prefix=resource_type) spec = {'tenant_id': tenant_id, 'name': name, 'ha': ha} if external_network: spec['external_gateway_info'] = {"network_id": external_network} return self._create_resource(resource_type, spec) def create_network(self, tenant_id, name=None, external=False): resource_type = 'network' name = name or utils.get_rand_name(prefix=resource_type) spec = {'tenant_id': tenant_id, 'name': name} spec['router:external'] = external return self._create_resource(resource_type, spec) def create_subnet(self, tenant_id, network_id, cidr, gateway_ip=None, name=None, enable_dhcp=True, ipv6_address_mode='slaac', ipv6_ra_mode='slaac'): resource_type = 'subnet' name = name or utils.get_rand_name(prefix=resource_type) ip_version = netaddr.IPNetwork(cidr).version spec = {'tenant_id': tenant_id, 'network_id': network_id, 'name': name, 'cidr': cidr, 'enable_dhcp': enable_dhcp, 'ip_version': ip_version} if ip_version == constants.IP_VERSION_6: spec['ipv6_address_mode'] = ipv6_address_mode spec['ipv6_ra_mode'] = ipv6_ra_mode if gateway_ip: spec['gateway_ip'] = gateway_ip return self._create_resource(resource_type, spec) def create_port(self, tenant_id, network_id, hostname=None, qos_policy_id=None, **kwargs): spec = { 'network_id': network_id, 'tenant_id': tenant_id, } spec.update(kwargs) if hostname is not None: spec[portbindings.HOST_ID] = hostname if qos_policy_id: spec['qos_policy_id'] = qos_policy_id return self._create_resource('port', spec) def create_floatingip(self, tenant_id, floating_network_id, fixed_ip_address, port_id): spec = { 'floating_network_id': floating_network_id, 'tenant_id': tenant_id, 'fixed_ip_address': fixed_ip_address, 'port_id': port_id } return self._create_resource('floatingip', spec) def add_router_interface(self, router_id, subnet_id): body = {'subnet_id': subnet_id} router_interface_info = self.client.add_interface_router( router=router_id, body=body) self.addCleanup(_safe_method(self.client.remove_interface_router), router=router_id, body=body) return router_interface_info def create_qos_policy(self, tenant_id, name, description, shared): policy = self.client.create_qos_policy( body={'policy': {'name': name, 'description': description, 'shared': shared, 'tenant_id': tenant_id}}) def detach_and_delete_policy(): qos_policy_id = policy['policy']['id'] ports_with_policy = self.client.list_ports( qos_policy_id=qos_policy_id)['ports'] for port in ports_with_policy: self.client.update_port( port['id'], body={'port': {'qos_policy_id': None}}) self.client.delete_qos_policy(qos_policy_id) # NOTE: We'll need to add support for detaching from network once # create_network() supports qos_policy_id. self.addCleanup(_safe_method(detach_and_delete_policy)) return policy['policy'] def create_bandwidth_limit_rule(self, tenant_id, qos_policy_id, limit=None, burst=None): rule = {'tenant_id': tenant_id} if limit: rule['max_kbps'] = limit if burst: rule['max_burst_kbps'] = burst rule = self.client.create_bandwidth_limit_rule( policy=qos_policy_id, body={'bandwidth_limit_rule': rule}) self.addCleanup(_safe_method(self.client.delete_bandwidth_limit_rule), rule['bandwidth_limit_rule']['id'], qos_policy_id) return rule['bandwidth_limit_rule'] def create_dscp_marking_rule(self, tenant_id, qos_policy_id, dscp_mark=0): rule = {'tenant_id': tenant_id} if dscp_mark: rule['dscp_mark'] = dscp_mark rule = self.client.create_dscp_marking_rule( policy=qos_policy_id, body={'dscp_marking_rule': rule}) self.addCleanup(_safe_method(self.client.delete_dscp_marking_rule), rule['dscp_marking_rule']['id'], qos_policy_id) return rule['dscp_marking_rule'] def create_trunk(self, tenant_id, port_id, name=None, admin_state_up=None, sub_ports=None): """Create a trunk via API. :param tenant_id: ID of the tenant. :param port_id: Parent port of trunk. :param name: Name of the trunk. :param admin_state_up: Admin state of the trunk. :param sub_ports: List of subport dictionaries in format {'port_id': , 'segmentation_type': 'vlan', 'segmentation_id': } :return: Dictionary with trunk's data returned from Neutron API. """ spec = { 'port_id': port_id, 'tenant_id': tenant_id, } if name is not None: spec['name'] = name if sub_ports is not None: spec['sub_ports'] = sub_ports if admin_state_up is not None: spec['admin_state_up'] = admin_state_up trunk = self.client.create_trunk({'trunk': spec})['trunk'] if sub_ports: self.addCleanup( _safe_method(self.trunk_remove_subports), tenant_id, trunk['id'], trunk['sub_ports']) self.addCleanup(_safe_method(self.client.delete_trunk), trunk['id']) return trunk def trunk_add_subports(self, tenant_id, trunk_id, sub_ports): """Add subports to the trunk. :param tenant_id: ID of the tenant. :param trunk_id: ID of the trunk. :param sub_ports: List of subport dictionaries to be added in format {'port_id': , 'segmentation_type': 'vlan', 'segmentation_id': } """ spec = { 'tenant_id': tenant_id, 'sub_ports': sub_ports, } trunk = self.client.trunk_add_subports(trunk_id, spec) sub_ports_to_remove = [ sub_port for sub_port in trunk['sub_ports'] if sub_port in sub_ports] self.addCleanup( _safe_method(self.trunk_remove_subports), tenant_id, trunk_id, sub_ports_to_remove) def trunk_remove_subports(self, tenant_id, trunk_id, sub_ports): """Remove subports from the trunk. :param trunk_id: ID of the trunk. :param sub_ports: List of subport port IDs. """ spec = { 'tenant_id': tenant_id, 'sub_ports': sub_ports, } return self.client.trunk_remove_subports(trunk_id, spec) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/config.py0000664000175000017500000002422600000000000026251 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 tempfile import fixtures from neutron_lib import constants from neutron.common import utils from neutron.plugins.ml2.extensions import qos as qos_ext from neutron.tests.common import config_fixtures from neutron.tests.common.exclusive_resources import port from neutron.tests.common import helpers as c_helpers class ConfigFixture(fixtures.Fixture): """A fixture that holds an actual Neutron configuration. Note that 'self.config' is intended to only be updated once, during the constructor, so if this fixture is re-used (setUp is called twice), then the dynamic configuration values won't change. The correct usage is initializing a new instance of the class. """ def __init__(self, env_desc, host_desc, temp_dir, base_filename): super(ConfigFixture, self).__init__() self.config = config_fixtures.ConfigDict() self.env_desc = env_desc self.host_desc = host_desc self.temp_dir = temp_dir self.base_filename = base_filename def _setUp(self): cfg_fixture = config_fixtures.ConfigFileFixture( self.base_filename, self.config, self.temp_dir) self.useFixture(cfg_fixture) self.filename = cfg_fixture.filename class NeutronConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, connection, rabbitmq_environment): super(NeutronConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='neutron.conf') service_plugins = ['router', 'trunk'] if env_desc.qos: service_plugins.append('qos') self.config.update({ 'DEFAULT': { 'host': self._generate_host(), 'state_path': self._generate_state_path(self.temp_dir), 'api_paste_config': self._generate_api_paste(), 'core_plugin': 'ml2', 'service_plugins': ','.join(service_plugins), 'auth_strategy': 'noauth', 'debug': 'True', 'transport_url': 'rabbit://%(user)s:%(password)s@%(host)s:5672/%(vhost)s' % {'user': rabbitmq_environment.user, 'password': rabbitmq_environment.password, 'host': rabbitmq_environment.host, 'vhost': rabbitmq_environment.vhost}, }, 'database': { 'connection': connection, }, 'oslo_concurrency': { 'lock_path': '$state_path/lock', }, 'oslo_policy': { 'policy_file': self._generate_policy_json(), }, }) def _setUp(self): self.config['DEFAULT'].update({ 'bind_port': self.useFixture( port.ExclusivePort(constants.PROTO_NAME_TCP)).port }) super(NeutronConfigFixture, self)._setUp() def _generate_host(self): return utils.get_rand_name(prefix='host-') def _generate_state_path(self, temp_dir): # Assume that temp_dir will be removed by the caller self.state_path = tempfile.mkdtemp(prefix='state_path', dir=temp_dir) return self.state_path def _generate_api_paste(self): return c_helpers.find_sample_file('api-paste.ini') def _generate_policy_json(self): return c_helpers.find_sample_file('policy.json') class ML2ConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, tenant_network_types): super(ML2ConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='ml2_conf.ini') mechanism_drivers = self.env_desc.mech_drivers if self.env_desc.l2_pop: mechanism_drivers += ',l2population' self.config.update({ 'ml2': { 'tenant_network_types': tenant_network_types, 'mechanism_drivers': mechanism_drivers, }, 'ml2_type_vlan': { 'network_vlan_ranges': 'physnet1:1000:2999', }, 'ml2_type_gre': { 'tunnel_id_ranges': '1:1000', }, 'ml2_type_vxlan': { 'vni_ranges': '1001:2000', }, }) if env_desc.qos: self.config['ml2']['extension_drivers'] =\ qos_ext.QOS_EXT_DRIVER_ALIAS class OVSConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, local_ip): super(OVSConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='openvswitch_agent.ini') self.tunneling_enabled = self.env_desc.tunneling_enabled self.config.update({ 'ovs': { 'local_ip': local_ip, 'integration_bridge': self._generate_integration_bridge(), 'of_interface': host_desc.of_interface, 'ovsdb_interface': host_desc.ovsdb_interface, }, 'securitygroup': { 'firewall_driver': 'noop', }, 'agent': { 'l2_population': str(self.env_desc.l2_pop), 'arp_responder': str(self.env_desc.arp_responder), } }) if self.tunneling_enabled: self.config['agent'].update({ 'tunnel_types': self.env_desc.network_type}) self.config['ovs'].update({ 'tunnel_bridge': self._generate_tunnel_bridge(), 'int_peer_patch_port': self._generate_int_peer(), 'tun_peer_patch_port': self._generate_tun_peer()}) else: self.config['ovs']['bridge_mappings'] = ( self._generate_bridge_mappings()) if env_desc.qos: self.config['agent']['extensions'] = 'qos' def _setUp(self): if self.config['ovs']['of_interface'] == 'native': self.config['ovs'].update({ 'of_listen_port': self.useFixture( port.ExclusivePort(constants.PROTO_NAME_TCP)).port }) super(OVSConfigFixture, self)._setUp() def _generate_bridge_mappings(self): return 'physnet1:%s' % utils.get_rand_device_name(prefix='br-eth') def _generate_integration_bridge(self): return utils.get_rand_device_name(prefix='br-int') def _generate_tunnel_bridge(self): return utils.get_rand_device_name(prefix='br-tun') def _generate_int_peer(self): return utils.get_rand_device_name(prefix='patch-tun') def _generate_tun_peer(self): return utils.get_rand_device_name(prefix='patch-int') def get_br_int_name(self): return self.config.ovs.integration_bridge def get_br_phys_name(self): return self.config.ovs.bridge_mappings.split(':')[1] def get_br_tun_name(self): return self.config.ovs.tunnel_bridge class LinuxBridgeConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, local_ip, physical_device_name): super(LinuxBridgeConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename="linuxbridge_agent.ini" ) self.config.update({ 'VXLAN': { 'enable_vxlan': str(self.env_desc.tunneling_enabled), 'local_ip': local_ip, 'l2_population': str(self.env_desc.l2_pop), } }) if env_desc.qos: self.config.update({ 'AGENT': { 'extensions': 'qos' } }) if self.env_desc.tunneling_enabled: self.config.update({ 'LINUX_BRIDGE': { 'bridge_mappings': self._generate_bridge_mappings( physical_device_name ) } }) else: self.config.update({ 'LINUX_BRIDGE': { 'physical_interface_mappings': self._generate_bridge_mappings( physical_device_name ) } }) def _generate_bridge_mappings(self, device_name): return 'physnet1:%s' % device_name class L3ConfigFixture(ConfigFixture): def __init__(self, env_desc, host_desc, temp_dir, integration_bridge=None): super(L3ConfigFixture, self).__init__( env_desc, host_desc, temp_dir, base_filename='l3_agent.ini') if host_desc.l2_agent_type == constants.AGENT_TYPE_OVS: self._prepare_config_with_ovs_agent(integration_bridge) elif host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: self._prepare_config_with_linuxbridge_agent() self.config['DEFAULT'].update({ 'debug': 'True', 'test_namespace_suffix': self._generate_namespace_suffix(), }) def _prepare_config_with_ovs_agent(self, integration_bridge): self.config.update({ 'DEFAULT': { 'interface_driver': ('neutron.agent.linux.interface.' 'OVSInterfaceDriver'), 'ovs_integration_bridge': integration_bridge, } }) def _prepare_config_with_linuxbridge_agent(self): self.config.update({ 'DEFAULT': { 'interface_driver': ('neutron.agent.linux.interface.' 'BridgeInterfaceDriver'), } }) def _generate_external_bridge(self): return utils.get_rand_device_name(prefix='br-ex') def _generate_namespace_suffix(self): return utils.get_rand_name(prefix='test') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/environment.py0000664000175000017500000003343200000000000027347 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 fixtures from neutron_lib import constants from neutronclient.common import exceptions as nc_exc from oslo_config import cfg from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils from neutron.plugins.ml2.drivers.linuxbridge.agent import \ linuxbridge_neutron_agent as lb_agent from neutron.tests.common.exclusive_resources import ip_address from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.common import net_helpers from neutron.tests.fullstack.resources import config from neutron.tests.fullstack.resources import process class EnvironmentDescription(object): """A set of characteristics of an environment setup. Does the setup, as a whole, support tunneling? How about l2pop? """ def __init__(self, network_type='vxlan', l2_pop=True, qos=False, mech_drivers='openvswitch,linuxbridge', arp_responder=False): self.network_type = network_type self.l2_pop = l2_pop self.qos = qos self.network_range = None self.mech_drivers = mech_drivers self.arp_responder = arp_responder @property def tunneling_enabled(self): return self.network_type in ('vxlan', 'gre') class HostDescription(object): """A set of characteristics of an environment Host. What agents should the host spawn? What mode should each agent operate under? """ def __init__(self, l3_agent=False, of_interface='ovs-ofctl', ovsdb_interface='vsctl', l2_agent_type=constants.AGENT_TYPE_OVS): self.l2_agent_type = l2_agent_type self.l3_agent = l3_agent self.of_interface = of_interface self.ovsdb_interface = ovsdb_interface class Host(fixtures.Fixture): """The Host class models a physical host running agents, all reporting with the same hostname. OpenStack installers or administrators connect compute nodes to the physical tenant network by connecting the provider bridges to their respective physical NICs. Or, if using tunneling, by configuring an IP address on the appropriate physical NIC. The Host class does the same with the connect_* methods. TODO(amuller): Add start/stop/restart methods that will start/stop/restart all of the agents on this host. Add a kill method that stops all agents and disconnects the host from other hosts. """ def __init__(self, env_desc, host_desc, test_name, neutron_config, central_data_bridge, central_external_bridge): self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_config = neutron_config self.central_data_bridge = central_data_bridge self.central_external_bridge = central_external_bridge self.host_namespace = None self.agents = {} # we need to cache already created "per network" bridges if linuxbridge # agent is used on host: self.network_bridges = {} def _setUp(self): self.local_ip = self.allocate_local_ip() if self.host_desc.l2_agent_type == constants.AGENT_TYPE_OVS: self.setup_host_with_ovs_agent() elif self.host_desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: self.setup_host_with_linuxbridge_agent() if self.host_desc.l3_agent: self.l3_agent = self.useFixture( process.L3AgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, self.l3_agent_cfg_fixture)) def setup_host_with_ovs_agent(self): agent_cfg_fixture = config.OVSConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.local_ip) self.useFixture(agent_cfg_fixture) br_phys = self.useFixture( net_helpers.OVSBridgeFixture( agent_cfg_fixture.get_br_phys_name())).bridge if self.env_desc.tunneling_enabled: self.useFixture( net_helpers.OVSBridgeFixture( agent_cfg_fixture.get_br_tun_name())).bridge self.connect_to_internal_network_via_tunneling() else: self.connect_to_internal_network_via_vlans(br_phys) self.ovs_agent = self.useFixture( process.OVSAgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, agent_cfg_fixture)) if self.host_desc.l3_agent: self.l3_agent_cfg_fixture = self.useFixture( config.L3ConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.ovs_agent.agent_cfg_fixture.get_br_int_name())) def setup_host_with_linuxbridge_agent(self): # First we need to provide connectivity for agent to prepare proper # bridge mappings in agent's config: self.host_namespace = self.useFixture( net_helpers.NamespaceFixture(prefix="host-") ).name self.connect_namespace_to_control_network() agent_cfg_fixture = config.LinuxBridgeConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir, self.local_ip, physical_device_name=self.host_port.name ) self.useFixture(agent_cfg_fixture) self.linuxbridge_agent = self.useFixture( process.LinuxBridgeAgentFixture( self.env_desc, self.host_desc, self.test_name, self.neutron_config, agent_cfg_fixture, namespace=self.host_namespace ) ) if self.host_desc.l3_agent: self.l3_agent_cfg_fixture = self.useFixture( config.L3ConfigFixture( self.env_desc, self.host_desc, self.neutron_config.temp_dir)) def _connect_ovs_port(self, cidr_address): ovs_device = self.useFixture( net_helpers.OVSPortFixture( bridge=self.central_data_bridge, namespace=self.host_namespace)).port # NOTE: This sets an IP address on the host's root namespace # which is cleaned up when the device is deleted. ovs_device.addr.add(cidr_address) return ovs_device def connect_namespace_to_control_network(self): self.host_port = self._connect_ovs_port( common_utils.ip_to_cidr(self.local_ip, 24) ) self.host_port.link.set_up() def connect_to_internal_network_via_tunneling(self): veth_1, veth_2 = self.useFixture( net_helpers.VethFixture()).ports # NOTE: This sets an IP address on the host's root namespace # which is cleaned up when the device is deleted. veth_1.addr.add(common_utils.ip_to_cidr(self.local_ip, 32)) veth_1.link.set_up() veth_2.link.set_up() def connect_to_internal_network_via_vlans(self, host_data_bridge): # If using VLANs as a segmentation device, it's needed to connect # a provider bridge to a centralized, shared bridge. net_helpers.create_patch_ports( self.central_data_bridge, host_data_bridge) def connect_to_external_network(self, host_external_bridge): net_helpers.create_patch_ports( self.central_external_bridge, host_external_bridge) def allocate_local_ip(self): if not self.env_desc.network_range: return str(self.useFixture( ip_address.ExclusiveIPAddress( '240.0.0.1', '240.255.255.254')).address) return str(self.useFixture( ip_address.ExclusiveIPAddress( str(self.env_desc.network_range[2]), str(self.env_desc.network_range[-2]))).address) def get_bridge(self, network_id): if "ovs" in self.agents.keys(): return self.ovs_agent.br_int elif "linuxbridge" in self.agents.keys(): bridge = self.network_bridges.get(network_id, None) if not bridge: br_prefix = lb_agent.LinuxBridgeManager.get_bridge_name( network_id) bridge = self.useFixture( net_helpers.LinuxBridgeFixture( prefix=br_prefix, namespace=self.host_namespace, prefix_is_full_name=True)).bridge self.network_bridges[network_id] = bridge return bridge @property def hostname(self): return self.neutron_config.config.DEFAULT.host @property def l3_agent(self): return self.agents['l3'] @l3_agent.setter def l3_agent(self, agent): self.agents['l3'] = agent @property def ovs_agent(self): return self.agents['ovs'] @ovs_agent.setter def ovs_agent(self, agent): self.agents['ovs'] = agent @property def linuxbridge_agent(self): return self.agents['linuxbridge'] @linuxbridge_agent.setter def linuxbridge_agent(self, agent): self.agents['linuxbridge'] = agent class Environment(fixtures.Fixture): """Represents a deployment topology. Environment is a collection of hosts. It starts a Neutron server and a parametrized number of Hosts, each a collection of agents. The Environment accepts a collection of HostDescription, each describing the type of Host to create. """ def __init__(self, env_desc, hosts_desc): """Environment, represents a deployment topology :param env_desc: An EnvironmentDescription instance. :param hosts_desc: A list of HostDescription instances. """ super(Environment, self).__init__() self.env_desc = env_desc self.hosts_desc = hosts_desc self.hosts = [] def wait_until_env_is_up(self): common_utils.wait_until_true(self._processes_are_ready) def _processes_are_ready(self): try: running_agents = self.neutron_server.client.list_agents()['agents'] agents_count = sum(len(host.agents) for host in self.hosts) return len(running_agents) == agents_count except nc_exc.NeutronClientException: return False def _create_host(self, host_desc): temp_dir = self.useFixture(fixtures.TempDir()).path neutron_config = config.NeutronConfigFixture( self.env_desc, host_desc, temp_dir, cfg.CONF.database.connection, self.rabbitmq_environment) self.useFixture(neutron_config) return self.useFixture( Host(self.env_desc, host_desc, self.test_name, neutron_config, self.central_data_bridge, self.central_external_bridge)) def _setUp(self): self.temp_dir = self.useFixture(fixtures.TempDir()).path # we need this bridge before rabbit and neutron service will start self.central_data_bridge = self.useFixture( net_helpers.OVSBridgeFixture('cnt-data')).bridge self.central_external_bridge = self.useFixture( net_helpers.OVSBridgeFixture('cnt-ex')).bridge # Get rabbitmq address (and cnt-data network) rabbitmq_ip_address = self._configure_port_for_rabbitmq() self.rabbitmq_environment = self.useFixture( process.RabbitmqEnvironmentFixture(host=rabbitmq_ip_address) ) plugin_cfg_fixture = self.useFixture( config.ML2ConfigFixture( self.env_desc, self.hosts_desc, self.temp_dir, self.env_desc.network_type)) neutron_cfg_fixture = self.useFixture( config.NeutronConfigFixture( self.env_desc, None, self.temp_dir, cfg.CONF.database.connection, self.rabbitmq_environment)) self.neutron_server = self.useFixture( process.NeutronServerFixture( self.env_desc, None, self.test_name, neutron_cfg_fixture, plugin_cfg_fixture)) self.hosts = [self._create_host(desc) for desc in self.hosts_desc] self.wait_until_env_is_up() def _configure_port_for_rabbitmq(self): self.env_desc.network_range = self._get_network_range() if not self.env_desc.network_range: return "127.0.0.1" rabbitmq_ip = str(self.env_desc.network_range[1]) rabbitmq_port = ip_lib.IPDevice(self.central_data_bridge.br_name) rabbitmq_port.addr.add(common_utils.ip_to_cidr(rabbitmq_ip, 24)) rabbitmq_port.link.set_up() return rabbitmq_ip def _get_network_range(self): # NOTE(slaweq): We need to choose IP address on which rabbitmq will be # available because LinuxBridge agents are spawned in their own # namespaces and need to know where the rabbitmq server is listening. # For ovs agent it is not necessary because agents are spawned in # globalscope together with rabbitmq server so default localhost # address is fine for them for desc in self.hosts_desc: if desc.l2_agent_type == constants.AGENT_TYPE_LINUXBRIDGE: return self.useFixture( ip_network.ExclusiveIPNetwork( "240.0.0.0", "240.255.255.255", "24")).network ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/machine.py0000664000175000017500000001364100000000000026407 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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_lib import constants from neutron.agent.linux import ip_lib from neutron.common import utils from neutron.extensions import portbindings as pbs from neutron.tests.common import machine_fixtures from neutron.tests.common import net_helpers class FakeFullstackMachinesList(list): """A list of items implementing the FakeFullstackMachine interface.""" def block_until_all_boot(self): for vm in self: vm.block_until_boot() def ping_all(self): # Generate an iterable of all unique pairs. For example: # itertools.combinations(range(3), 2) results in: # ((0, 1), (0, 2), (1, 2)) for vm_1, vm_2 in itertools.combinations(self, 2): vm_1.block_until_ping(vm_2.ip) class FakeFullstackMachine(machine_fixtures.FakeMachineBase): def __init__(self, host, network_id, tenant_id, safe_client, neutron_port=None, bridge_name=None): super(FakeFullstackMachine, self).__init__() self.host = host self.tenant_id = tenant_id self.network_id = network_id self.safe_client = safe_client self.neutron_port = neutron_port self.bridge_name = bridge_name def _setUp(self): super(FakeFullstackMachine, self)._setUp() self.bridge = self._get_bridge() if not self.neutron_port: self.neutron_port = self.safe_client.create_port( network_id=self.network_id, tenant_id=self.tenant_id, hostname=self.host.hostname) mac_address = self.neutron_port['mac_address'] hybrid_plug = self.neutron_port[pbs.VIF_DETAILS].get( pbs.OVS_HYBRID_PLUG, False) self.bind_port_if_needed() self.port = self.useFixture( net_helpers.PortFixture.get( self.bridge, self.namespace, mac_address, self.neutron_port['id'], hybrid_plug)).port for fixed_ip in self.neutron_port['fixed_ips']: self._configure_ipaddress(fixed_ip) def bind_port_if_needed(self): if self.neutron_port[pbs.VIF_TYPE] == pbs.VIF_TYPE_UNBOUND: self.safe_client.client.update_port( self.neutron_port['id'], {'port': {pbs.HOST_ID: self.host.hostname}}) self.addCleanup(self.safe_client.client.update_port, self.neutron_port['id'], {'port': {pbs.HOST_ID: ''}}) def _get_bridge(self): if self.bridge_name is None: return self.host.get_bridge(self.network_id) agent_type = self.host.host_desc.l2_agent_type if agent_type == constants.AGENT_TYPE_OVS: new_bridge = self.useFixture( net_helpers.OVSTrunkBridgeFixture(self.bridge_name)).bridge else: raise NotImplementedError( "Support for %s agent is not implemented." % agent_type) return new_bridge def _configure_ipaddress(self, fixed_ip): if (netaddr.IPAddress(fixed_ip['ip_address']).version == constants.IP_VERSION_6): # v6Address/default_route is auto-configured. self._ipv6 = fixed_ip['ip_address'] else: self._ip = fixed_ip['ip_address'] subnet_id = fixed_ip['subnet_id'] subnet = self.safe_client.client.show_subnet(subnet_id) prefixlen = netaddr.IPNetwork(subnet['subnet']['cidr']).prefixlen self._ip_cidr = '%s/%s' % (self._ip, prefixlen) # TODO(amuller): Support DHCP self.port.addr.add(self.ip_cidr) self.gateway_ip = subnet['subnet']['gateway_ip'] if self.gateway_ip: net_helpers.set_namespace_gateway(self.port, self.gateway_ip) @property def ipv6(self): return self._ipv6 @property def ip(self): return self._ip @property def ip_cidr(self): return self._ip_cidr def block_until_boot(self): utils.wait_until_true( lambda: (self.safe_client.client.show_port(self.neutron_port['id']) ['port']['status'] == 'ACTIVE'), sleep=3) def destroy(self): """Destroy this fake machine. This should simulate deletion of a vm. It doesn't call cleanUp(). """ self.safe_client.client.update_port( self.neutron_port['id'], {'port': {pbs.HOST_ID: ''}} ) # All associated vlan interfaces are deleted too self.bridge.delete_port(self.port.name) ip_wrap = ip_lib.IPWrapper(self.namespace) ip_wrap.netns.delete(self.namespace) class FakeFullstackTrunkMachine(FakeFullstackMachine): def __init__(self, trunk, *args, **kwargs): super(FakeFullstackTrunkMachine, self).__init__(*args, **kwargs) self.trunk = trunk def add_vlan_interface(self, mac_address, ip_address, segmentation_id): """Add VLAN interface to VM's namespace. :param mac_address: MAC address to be set on VLAN interface. :param ip_address: The IPNetwork instance containing IP address assigned to the interface. :param segmentation_id: VLAN tag added to the interface. """ net_helpers.create_vlan_interface( self.namespace, self.port.name, mac_address, ip_address, segmentation_id) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/resources/process.py0000664000175000017500000002031500000000000026455 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 datetime import os import shutil import signal import fixtures from neutronclient.common import exceptions as nc_exc from neutronclient.v2_0 import client from neutron.agent.common import async_process from neutron.agent.linux import utils from neutron.common import utils as common_utils from neutron.tests import base from neutron.tests.common import net_helpers from neutron.tests.fullstack import base as fullstack_base class ProcessFixture(fixtures.Fixture): def __init__(self, test_name, process_name, exec_name, config_filenames, namespace=None, kill_signal=signal.SIGKILL): super(ProcessFixture, self).__init__() self.test_name = test_name self.process_name = process_name self.exec_name = exec_name self.config_filenames = config_filenames self.process = None self.kill_signal = kill_signal self.namespace = namespace def _setUp(self): self.start() self.addCleanup(self.stop) def start(self): test_name = base.sanitize_log_path(self.test_name) log_dir = os.path.join(fullstack_base.DEFAULT_LOG_DIR, test_name) common_utils.ensure_dir(log_dir) timestamp = datetime.datetime.now().strftime("%Y-%m-%d--%H-%M-%S-%f") log_file = "%s--%s.log" % (self.process_name, timestamp) cmd = [shutil.which(self.exec_name), '--log-dir', log_dir, '--log-file', log_file] for filename in self.config_filenames: cmd += ['--config-file', filename] run_as_root = bool(self.namespace) self.process = async_process.AsyncProcess( cmd, run_as_root=run_as_root, namespace=self.namespace ) self.process.start(block=True) def stop(self): self.process.stop(block=True, kill_signal=self.kill_signal) class RabbitmqEnvironmentFixture(fixtures.Fixture): def __init__(self, host="127.0.0.1"): super(RabbitmqEnvironmentFixture, self).__init__() self.host = host def _setUp(self): self.user = common_utils.get_rand_name(prefix='user') self.password = common_utils.get_rand_name(prefix='pass') self.vhost = common_utils.get_rand_name(prefix='vhost') self._execute('add_user', self.user, self.password) self.addCleanup(self._execute, 'delete_user', self.user) self._execute('add_vhost', self.vhost) self.addCleanup(self._execute, 'delete_vhost', self.vhost) self._execute('set_permissions', '-p', self.vhost, self.user, '.*', '.*', '.*') def _execute(self, *args): cmd = ['rabbitmqctl'] cmd.extend(args) utils.execute(cmd, run_as_root=True) class NeutronServerFixture(fixtures.Fixture): NEUTRON_SERVER = "neutron-server" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, plugin_cfg_fixture): super(NeutronServerFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.plugin_cfg_fixture = plugin_cfg_fixture def _setUp(self): config_filenames = [self.neutron_cfg_fixture.filename, self.plugin_cfg_fixture.filename] self.process_fixture = self.useFixture(ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_SERVER, exec_name=self.NEUTRON_SERVER, config_filenames=config_filenames, kill_signal=signal.SIGTERM)) common_utils.wait_until_true(self.server_is_live) def server_is_live(self): try: self.client.list_networks() return True except nc_exc.NeutronClientException: return False @property def client(self): url = ("http://127.0.0.1:%s" % self.neutron_cfg_fixture.config.DEFAULT.bind_port) return client.Client(auth_strategy="noauth", endpoint_url=url) class OVSAgentFixture(fixtures.Fixture): NEUTRON_OVS_AGENT = "neutron-openvswitch-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture): super(OVSAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_config = self.neutron_cfg_fixture.config self.agent_cfg_fixture = agent_cfg_fixture self.agent_config = agent_cfg_fixture.config def _setUp(self): self.br_int = self.useFixture( net_helpers.OVSBridgeFixture( self.agent_cfg_fixture.get_br_int_name())).bridge config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] self.process_fixture = self.useFixture(ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_OVS_AGENT, exec_name=shutil.which( 'ovs_agent.py', path=os.path.join(base.ROOTDIR, 'common', 'agents')), config_filenames=config_filenames, kill_signal=signal.SIGTERM)) class LinuxBridgeAgentFixture(fixtures.Fixture): NEUTRON_LINUXBRIDGE_AGENT = "neutron-linuxbridge-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, agent_cfg_fixture, namespace=None): super(LinuxBridgeAgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.neutron_config = self.neutron_cfg_fixture.config self.agent_cfg_fixture = agent_cfg_fixture self.agent_config = agent_cfg_fixture.config self.namespace = namespace def _setUp(self): config_filenames = [self.neutron_cfg_fixture.filename, self.agent_cfg_fixture.filename] self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_LINUXBRIDGE_AGENT, exec_name=self.NEUTRON_LINUXBRIDGE_AGENT, config_filenames=config_filenames, namespace=self.namespace ) ) class L3AgentFixture(fixtures.Fixture): NEUTRON_L3_AGENT = "neutron-l3-agent" def __init__(self, env_desc, host_desc, test_name, neutron_cfg_fixture, l3_agent_cfg_fixture, namespace=None): super(L3AgentFixture, self).__init__() self.env_desc = env_desc self.host_desc = host_desc self.test_name = test_name self.neutron_cfg_fixture = neutron_cfg_fixture self.l3_agent_cfg_fixture = l3_agent_cfg_fixture self.namespace = namespace def _setUp(self): self.plugin_config = self.l3_agent_cfg_fixture.config config_filenames = [self.neutron_cfg_fixture.filename, self.l3_agent_cfg_fixture.filename] self.process_fixture = self.useFixture( ProcessFixture( test_name=self.test_name, process_name=self.NEUTRON_L3_AGENT, exec_name=shutil.which( 'l3_agent.py', path=os.path.join(base.ROOTDIR, 'common', 'agents')), config_filenames=config_filenames, namespace=self.namespace ) ) def get_namespace_suffix(self): return self.plugin_config.DEFAULT.test_namespace_suffix ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/test_l3_agent.py0000664000175000017500000001632200000000000025523 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 functools import netaddr from neutron_lib import constants from oslo_utils import uuidutils from neutron.agent.l3 import agent as l3_agent from neutron.agent.l3 import namespaces from neutron.agent.linux import ip_lib from neutron.common import utils as common_utils from neutron.tests.common.exclusive_resources import ip_network from neutron.tests.common import machine_fixtures from neutron.tests.fullstack import base from neutron.tests.fullstack.resources import environment from neutron.tests.fullstack.resources import machine from neutron.tests.unit import testlib_api load_tests = testlib_api.module_load_tests class TestL3Agent(base.BaseFullStackTestCase): def _create_external_network_and_subnet(self, tenant_id): network = self.safe_client.create_network( tenant_id, name='public', external=True) cidr = self.useFixture( ip_network.ExclusiveIPNetwork( "240.0.0.0", "240.255.255.255", "24")).network subnet = self.safe_client.create_subnet( tenant_id, network['id'], cidr, enable_dhcp=False) return network, subnet def block_until_port_status_active(self, port_id): def is_port_status_active(): port = self.client.show_port(port_id) return port['port']['status'] == 'ACTIVE' common_utils.wait_until_true(lambda: is_port_status_active(), sleep=1) def _create_net_subnet_and_vm(self, tenant_id, subnet_cidrs, host, router): network = self.safe_client.create_network(tenant_id) for cidr in subnet_cidrs: # For IPv6 subnets, enable_dhcp should be set to true. enable_dhcp = (netaddr.IPNetwork(cidr).version == constants.IP_VERSION_6) subnet = self.safe_client.create_subnet( tenant_id, network['id'], cidr, enable_dhcp=enable_dhcp) router_interface_info = self.safe_client.add_router_interface( router['id'], subnet['id']) self.block_until_port_status_active( router_interface_info['port_id']) vm = self.useFixture( machine.FakeFullstackMachine( host, network['id'], tenant_id, self.safe_client)) vm.block_until_boot() return vm class TestLegacyL3Agent(TestL3Agent): def setUp(self): host_descriptions = [ environment.HostDescription(l3_agent=True), environment.HostDescription()] env = environment.Environment( environment.EnvironmentDescription( network_type='vlan', l2_pop=False), host_descriptions) super(TestLegacyL3Agent, self).setUp(env) def _get_namespace(self, router_id): return namespaces.build_ns_name(l3_agent.NS_PREFIX, router_id) def _assert_namespace_exists(self, ns_name): ip = ip_lib.IPWrapper(ns_name) common_utils.wait_until_true(lambda: ip.netns.exists(ns_name)) def test_namespace_exists(self): tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id) network = self.safe_client.create_network(tenant_id) subnet = self.safe_client.create_subnet( tenant_id, network['id'], '20.0.0.0/24', gateway_ip='20.0.0.1') self.safe_client.add_router_interface(router['id'], subnet['id']) namespace = "%s@%s" % ( self._get_namespace(router['id']), self.environment.hosts[0].l3_agent.get_namespace_suffix(), ) self._assert_namespace_exists(namespace) def test_east_west_traffic(self): tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id) vm1 = self._create_net_subnet_and_vm( tenant_id, ['20.0.0.0/24', '2001:db8:aaaa::/64'], self.environment.hosts[0], router) vm2 = self._create_net_subnet_and_vm( tenant_id, ['21.0.0.0/24', '2001:db8:bbbb::/64'], self.environment.hosts[1], router) vm1.block_until_ping(vm2.ip) # Verify ping6 from vm2 to vm1 IPv6 Address vm2.block_until_ping(vm1.ipv6) def test_snat_and_floatingip(self): # This function creates external network and boots an extrenal vm # on it with gateway ip and connected to central_external_bridge. # Later it creates a tenant vm on tenant network, with tenant router # connected to tenant network and external network. # To test snat and floatingip, try ping between tenant and external vms tenant_id = uuidutils.generate_uuid() ext_net, ext_sub = self._create_external_network_and_subnet(tenant_id) external_vm = self.useFixture( machine_fixtures.FakeMachine( self.environment.central_external_bridge, common_utils.ip_to_cidr(ext_sub['gateway_ip'], 24))) router = self.safe_client.create_router(tenant_id, external_network=ext_net['id']) vm = self._create_net_subnet_and_vm( tenant_id, ['20.0.0.0/24'], self.environment.hosts[1], router) # ping external vm to test snat vm.block_until_ping(external_vm.ip) fip = self.safe_client.create_floatingip( tenant_id, ext_net['id'], vm.ip, vm.neutron_port['id']) # ping floating ip from external vm external_vm.block_until_ping(fip['floating_ip_address']) class TestHAL3Agent(base.BaseFullStackTestCase): def setUp(self): host_descriptions = [ environment.HostDescription(l3_agent=True) for _ in range(2)] env = environment.Environment( environment.EnvironmentDescription( network_type='vxlan', l2_pop=True), host_descriptions) super(TestHAL3Agent, self).setUp(env) def _is_ha_router_active_on_one_agent(self, router_id): agents = self.client.list_l3_agent_hosting_routers(router_id) return ( agents['agents'][0]['ha_state'] != agents['agents'][1]['ha_state']) def test_ha_router(self): # TODO(amuller): Test external connectivity before and after a # failover, see: https://review.openstack.org/#/c/196393/ tenant_id = uuidutils.generate_uuid() router = self.safe_client.create_router(tenant_id, ha=True) agents = self.client.list_l3_agent_hosting_routers(router['id']) self.assertEqual(2, len(agents['agents']), 'HA router must be scheduled to both nodes') common_utils.wait_until_true( functools.partial( self._is_ha_router_active_on_one_agent, router['id']), timeout=90) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/fullstack/utils.py0000664000175000017500000000222400000000000024124 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. def get_ovs_interface_scenarios(): return [ ('openflow-cli_ovsdb-cli', {'of_interface': 'ovs-ofctl', 'ovsdb_interface': 'vsctl'}), ('openflow-native_ovsdb-cli', {'of_interface': 'native', 'ovsdb_interface': 'vsctl'}), ('openflow-cli_ovsdb-native', {'of_interface': 'ovs-ofctl', 'ovsdb_interface': 'native'}), ('openflow-native_ovsdb-native', {'of_interface': 'native', 'ovsdb_interface': 'native'}), ] ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/0000775000175000017500000000000000000000000022564 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/__init__.py0000664000175000017500000000000000000000000024663 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/db/0000775000175000017500000000000000000000000023151 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/db/__init__.py0000664000175000017500000000000000000000000025250 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/db/test_migrations.py0000664000175000017500000000757100000000000026750 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 alembic import script as alembic_script from neutron.db.migration.alembic_migrations import external from neutron.db.migration import cli as migration from neutron.tests.functional.db import test_migrations from neutron.tests.unit import testlib_api from oslo_config import cfg import sqlalchemy from neutron_fwaas.db.models import head # EXTERNAL_TABLES should contain all names of tables that are not related to # current repo. EXTERNAL_TABLES = set(external.TABLES) # Model moved to vendor repo EXTERNAL_TABLES.update({'cisco_firewall_associations'}) EXTERNAL_TABLES.update({'firewall_router_associations'}) VERSION_TABLE = 'alembic_version_fwaas' class _TestModelsMigrationsFWaaS(test_migrations._TestModelsMigrations): def db_sync(self, engine): cfg.CONF.set_override('connection', engine.url, group='database') for conf in migration.get_alembic_configs(): self.alembic_config = conf self.alembic_config.neutron_config = cfg.CONF migration.do_alembic_command(conf, 'upgrade', 'heads') def get_metadata(self): return head.get_metadata() def include_object(self, object_, name, type_, reflected, compare_to): if type_ == 'table' and (name.startswith('alembic') or name == VERSION_TABLE or name in EXTERNAL_TABLES): return False if type_ == 'index' and reflected and name.startswith("idx_autoinc_"): return False return True class TestModelsMigrationsMysql(testlib_api.MySQLTestCaseMixin, _TestModelsMigrationsFWaaS, testlib_api.SqlTestCaseLight): pass class TestModelsMigrationsPostgresql(testlib_api.PostgreSQLTestCaseMixin, _TestModelsMigrationsFWaaS, testlib_api.SqlTestCaseLight): pass class TestSanityCheck(testlib_api.SqlTestCaseLight): BUILD_SCHEMA = False def setUp(self): super(TestSanityCheck, self).setUp() for conf in migration.get_alembic_configs(): self.alembic_config = conf self.alembic_config.neutron_config = cfg.CONF def _drop_table(self, table): with self.engine.begin() as conn: table.drop(conn) def test_check_sanity_f24e0d5e5bff(self): current_revision = "f24e0d5e5bff" fwg_port_association = sqlalchemy.Table( 'firewall_group_port_associations_v2', sqlalchemy.MetaData(), sqlalchemy.Column('firewall_group_id', sqlalchemy.String(36)), sqlalchemy.Column('port_id', sqlalchemy.String(36))) with self.engine.connect() as conn: fwg_port_association.create(conn) self.addCleanup(self._drop_table, fwg_port_association) conn.execute(fwg_port_association.insert(), [ {'firewall_group_id': '1234', 'port_id': '12345'}, {'firewall_group_id': '12343', 'port_id': '12345'} ]) script_dir = alembic_script.ScriptDirectory.from_config( self.alembic_config) script = script_dir.get_revision(current_revision).module self.assertRaises( script.DuplicatePortRecordinFirewallGroupPortAssociation, script.check_sanity, conn) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/privileged/0000775000175000017500000000000000000000000024716 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/privileged/__init__.py0000664000175000017500000000000000000000000027015 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/privileged/test_dummy.py0000664000175000017500000000150100000000000027457 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.tests.functional import base from neutron_fwaas.privileged.tests.functional import dummy class DummyTest(base.BaseSudoTestCase): def test_dummy(self): dummy.dummy() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/privileged/test_netlink_lib.py0000664000175000017500000001632700000000000030632 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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.agent.linux import utils as linux_utils from neutron.tests.common import net_helpers from neutron.tests.functional import base as functional_base from oslo_log import log as logging import neutron_fwaas.privileged.netlink_lib as nl_lib LOG = logging.getLogger(__name__) def check_nf_conntrack_ipv6_is_loaded(): try: output = linux_utils.execute(['lsmod']) except RuntimeError: msg = "Failed execute command lsmod!" raise RuntimeError(msg) if 'nf_conntrack' in output: return True return False def _create_entries(namespace, conntrack_cmds): for cmd in conntrack_cmds: exec_cmd = ['ip', 'netns', 'exec', namespace] + cmd try: linux_utils.execute(exec_cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1], privsep_exec=True) except RuntimeError: raise Exception('Error while creating entry') class NetlinkLibTestCase(functional_base.BaseSudoTestCase): """Functional test for netlink_lib: List, delete, flush conntrack entries. For each function, first we add a specific namespace, then create real conntrack entries. netlink_lib function will do list, delete and flush these entries. This class will test this netlink_lib function work as expected. """ CONNTRACK_CMDS = ( ['conntrack', '-I', '-p', 'tcp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--sport', '1', '--dport', '2', '--state', 'ESTABLISHED', '--timeout', '1234'], ['conntrack', '-I', '-p', 'udp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--sport', '1', '--dport', '2', '--timeout', '1234'], ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234'], ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234'], ) def test_list_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) expected = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ) entries_list = nl_lib.list_entries(namespace=namespace) self.assertEqual(expected, entries_list) def _delete_entry(self, delete_entries, remain_entries): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.delete_entries(namespace=namespace, entries=delete_entries) entries_list = nl_lib.list_entries(namespace) self.assertEqual(remain_entries, entries_list) def test_delete_icmp_entry(self): icmp_entry = [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333)] remain_entries = ( (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2'), ) self._delete_entry(icmp_entry, remain_entries) def test_delete_tcp_entry(self): tcp_entry = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ) self._delete_entry(tcp_entry, remain_entries) def test_delete_udp_entry(self): udp_entry = [(4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2')] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') ) self._delete_entry(udp_entry, remain_entries) def test_delete_multiple_entries(self): delete_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ) remain_entries = () self._delete_entry(delete_entries, remain_entries) def test_flush_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.flush_entries(namespace) entries_list = nl_lib.list_entries(namespace) self.assertEqual((), entries_list) class NetlinkLibTestCaseIPv6(functional_base.BaseSudoTestCase): CONNTRACK_CMDS = ( ['conntrack', '-I', '-p', 'icmp', '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-type', '8', '--icmp-code', '0', '--icmp-id', '3333', '--timeout', '1234'], ['conntrack', '-I', '-p', 'icmpv6', '-s', '10::10', '-d', '20::20', '--icmpv6-type', '128', '--icmpv6-code', '0', '--icmpv6-id', '3456', '--timeout', '1234'], ) def setUp(self): super(NetlinkLibTestCaseIPv6, self).setUp() if not check_nf_conntrack_ipv6_is_loaded(): self.skipTest( "nf_conntrack_ipv6 module wasn't loaded. Please load" "this module into your system if you want to use " "netlink conntrack with ipv6" ) def test_list_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) expected = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), (6, 'icmpv6', 128, 0, '10::10', '20::20', 3456), ) entries_list = nl_lib.list_entries(namespace=namespace) self.assertEqual(expected, entries_list) def _delete_entry(self, delete_entries, remain_entries): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.delete_entries(namespace=namespace, entries=delete_entries) entries_list = nl_lib.list_entries(namespace) self.assertEqual(remain_entries, entries_list) def test_delete_icmpv6_entry(self): icmp_entry = [(6, 'icmpv6', 128, 0, '10::10', '20::20', 3456)] remain_entries = ( (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', 3333), ) self._delete_entry(icmp_entry, remain_entries) def test_flush_entries(self): namespace = self.useFixture(net_helpers.NamespaceFixture()).name _create_entries(namespace, self.CONNTRACK_CMDS) nl_lib.flush_entries(namespace) entries_list = nl_lib.list_entries(namespace) self.assertEqual((), entries_list) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/privileged/test_utils.py0000664000175000017500000000433000000000000027467 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutron.agent.linux import ip_lib from neutron.common import utils as neutron_utils from neutron.tests.common import net_helpers from neutron.tests.functional import base from neutron_fwaas.privileged.tests.functional import utils class InNamespaceTest(base.BaseSudoTestCase): def setUp(self): super(InNamespaceTest, self).setUp() self.namespace = self.useFixture(net_helpers.NamespaceFixture()).name ip = ip_lib.IPWrapper() root_dev_name = neutron_utils.get_rand_device_name() netns_dev_name = neutron_utils.get_rand_device_name() self.root_dev, self.netns_dev = ip.add_veth( root_dev_name, netns_dev_name, namespace2=self.namespace) self.addCleanup(self.root_dev.link.delete) def test_in_namespace(self): before, observed, after = utils.get_in_namespace_interfaces( self.namespace) expected = ['lo', self.netns_dev.name] self.assertItemsEqual(expected, observed) # Other tests can create/delete devices, so we just checks # self.root_dev_name is included in the root namespace result. self.assertIn(self.root_dev.name, before) self.assertIn(self.root_dev.name, after) def test_in_no_namespace(self): before, observed, after = utils.get_in_namespace_interfaces(None) # Other tests can create/delete devices, so we just checks # self.root_dev_name is included in the root namespace result. self.assertIn(self.root_dev.name, observed) self.assertIn(self.root_dev.name, before) self.assertIn(self.root_dev.name, after) ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/0000775000175000017500000000000000000000000024407 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/__init__.py0000664000175000017500000000000000000000000026506 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/0000775000175000017500000000000000000000000025662 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/__init__.py0000664000175000017500000000000000000000000027761 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/0000775000175000017500000000000000000000000027143 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/__init__.py0000664000175000017500000000000000000000000031242 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.317351 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/0000775000175000017500000000000000000000000030621 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/__init__.py0000664000175000017500000000000000000000000032720 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/0000775000175000017500000000000000000000000032424 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/__init__0000664000175000017500000000000000000000000034074 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020700000000000011454 xustar0000000000000000113 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/test_log.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/test_log0000664000175000017500000006067500000000000034205 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited. # 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 time from neutron.agent.l3 import l3_agent_extension_api as l3_ext_api from neutron.agent.linux import utils as linux_utils from neutron.tests.functional.agent.l3 import framework from neutron_lib import constants from neutron_lib import context as neutron_context from neutron_lib.services.logapi import constants as log_const from oslo_config import cfg from oslo_log import log as logging from neutron_fwaas.services.logapi.agents.drivers.iptables import log LOG = logging.getLogger(__name__) FAKE_LOG_ID = 'fake_log_id' FAKE_PROJECT_ID = 'fake_project_id' FAKE_RESOURCE_TYPE = 'firewall_group' # Default chain name ACCEPTED_CHAIN = 'accepted' DROPPED_CHAIN = 'dropped' REJECTED_CHAIN = 'rejected' ACCEPT = 'ACCEPT' DROP = 'DROP' REJECT = 'REJECT' ALL = 'ALL' CHAIN_NAME_POSTFIX_MAP = { ACCEPT: ACCEPTED_CHAIN, DROP: DROPPED_CHAIN, REJECT: REJECTED_CHAIN } FWAAS_V2_LOG_OPTS = [ cfg.StrOpt('extensions', default=['fwaas_v2', 'fwaas_v2_log']), ] AGENT_MODE_OPTS = [ cfg.StrOpt('agent_mode', default='legacy', choices=['legacy', 'dvr', 'dvr_snat', 'dvr_no_external']), ] class FWLoggingTestBase(framework.L3AgentTestFramework): def setUp(self): super(FWLoggingTestBase, self).setUp() self.conf.register_opts(FWAAS_V2_LOG_OPTS, 'fwaas') self.conf.register_opts(AGENT_MODE_OPTS, group='DEFAULT') self._set_agent_mode(self.conf) self.if_prefix = 'qr-' self.context = neutron_context.get_admin_context() self.context.tenant_id = FAKE_PROJECT_ID self.resource_rpc = mock.patch( 'neutron.services.logapi.rpc.agent.LoggingApiStub').start() # Initialize logging driver self.log_driver = self._initialize_iptables_log() # Prepare router_info self._prepare_router_info(n_ports=2) def _prepare_router_info(self, n_ports=0): router_data = self.generate_router_info(enable_ha=False, num_internal_ports=n_ports) self.router_info = self.manage_router(self.agent, router_data) self.log_driver.agent_api._router_info = { self.router_info.router_id: self.router_info } def _initialize_iptables_log(self): self.agent_api = l3_ext_api.L3AgentExtensionAPI({}, None) log_driver = log.IptablesLoggingDriver(self.agent_api) log_driver.initialize(self.resource_rpc) log_driver.conf = self.conf return log_driver def _refresh_logging_config(self, ipt_mgr): # Reset configuration for the next testing EVENT self.log_driver.ipt_mgr_list.clear() self.log_driver.fwg_port_logs.clear() self.log_driver.prefixes_table.clear() self.log_driver.cleanup_table.clear() self.log_driver.nflog_proc_map.clear() self.log_driver.unused_port_ids.clear() # Empty default chains self._empty_default_chains_v4v6(ipt_mgr=ipt_mgr) def _set_agent_mode(self, cfg, agent_mode='legacy'): cfg.agent_mode = agent_mode def _config_default_chains_v4v6(self, ipt_mgr): # Config default chains in iptables and ip6tables for action, chain in CHAIN_NAME_POSTFIX_MAP.items(): v4rules_in_chain = \ ipt_mgr.get_chain("filter", chain, ip_version=4) if not v4rules_in_chain: ipt_mgr.ipv4['filter'].add_chain(chain) ipt_mgr.ipv4['filter'].add_rule(chain, '-j %s' % action) v6rules_in_chain = \ ipt_mgr.get_chain("filter", chain, ip_version=6) if not v6rules_in_chain: ipt_mgr.ipv6['filter'].add_chain(chain) ipt_mgr.ipv6['filter'].add_rule(chain, '-j %s' % action) def _empty_default_chains_v4v6(self, ipt_mgr): # Empty default chains in iptables and ip6tables for action, chain in CHAIN_NAME_POSTFIX_MAP.items(): ipt_mgr.ipv4['filter'].empty_chain(chain=chain) ipt_mgr.ipv6['filter'].empty_chain(chain=chain) def _fake_log_resource(self, tenant_id, resource_id=None, target_id=None, event='ALL', enabled=True): log_resource = { 'id': FAKE_LOG_ID, 'name': 'fake_log_name', 'resource_type': FAKE_RESOURCE_TYPE, 'project_id': tenant_id, 'event': event, 'enabled': True} if resource_id: log_resource['resource_id'] = resource_id if target_id: log_resource['target_id'] = target_id if not enabled: log_resource['enabled'] = enabled return log_resource def _fake_log_info(self, log_id, port_ids, event='ALL'): return { 'event': event, 'id': log_id, 'project_id': FAKE_PROJECT_ID, 'ports_log': port_ids } def _get_expected_nflog_rule(self, wrap_name, if_prefix, logs_info): # Generate an expected NFLOG rules from given log_info rules = set() limit = 'limit --limit %s/sec --limit-burst %s' % \ (self.log_driver.rate_limit, self.log_driver.burst_limit) accept_chain = wrap_name + '-' + ACCEPTED_CHAIN drop_chain = wrap_name + '-' + DROPPED_CHAIN reject_chain = wrap_name + '-' + REJECTED_CHAIN for log_info in logs_info: event = log_info['event'] ports_log = log_info['ports_log'] for port_id in ports_log: device = (if_prefix + port_id)[:constants.LINUX_DEV_LEN] if event in [ACCEPT, ALL]: # Generate iptables rules for ACCEPT action prefix = self._get_log_prefix(port_id, ACCEPT) rules.add('-A %s -i %s -m %s -j NFLOG --nflog-prefix %s' % (accept_chain, device, limit, prefix.id)) rules.add('-A %s -o %s -m %s -j NFLOG --nflog-prefix %s' % (accept_chain, device, limit, prefix.id)) if event in [DROP, ALL]: # Generate iptables rules for DROP action prefix = self._get_log_prefix(port_id, DROP) rules.add('-A %s -i %s -m %s -j NFLOG --nflog-prefix %s' % (drop_chain, device, limit, prefix.id)) rules.add('-A %s -o %s -m %s -j NFLOG --nflog-prefix %s' % (drop_chain, device, limit, prefix.id)) # Generate iptables rules for REJECT action rules.add('-A %s -i %s -m %s -j NFLOG --nflog-prefix %s' % (reject_chain, device, limit, prefix.id)) rules.add('-A %s -o %s -m %s -j NFLOG --nflog-prefix %s' % (reject_chain, device, limit, prefix.id)) return rules def _get_log_prefix(self, port_id, action): prefix_table = self.log_driver.prefixes_table if port_id in prefix_table: for prefix in prefix_table[port_id]: if prefix.action == action: return prefix return None def _get_nflog_entries(self, namespace, table='iptables', chain_name=None): # Get NFLOG entries from iptables and ip6tables exec_cmd = ['ip', 'netns', 'exec', namespace, table, '-S'] if chain_name: exec_cmd += [chain_name] while True: try: output = linux_utils.execute(exec_cmd, run_as_root=True, check_exit_code=True, extra_ok_codes=[1], privsep_exec=True) nflog_rules = [rule for rule in output.splitlines() if 'NFLOG' in rule] return nflog_rules except RuntimeError: time.sleep(1) def assert_logging_results(self, ipt_mgr, log_info): # Comparing between expected NFLOG rules and NFLOG rules from iptables v4_rules = v6_rules = self._get_expected_nflog_rule( ipt_mgr.wrap_name, self.if_prefix, log_info) v4_actual = self._get_nflog_entries(ipt_mgr.namespace, table='iptables') v6_actual = self._get_nflog_entries(ipt_mgr.namespace, table='ip6tables') self.assertEqual(sorted(v4_rules), sorted(v4_actual)) self.assertEqual(sorted(v6_rules), sorted(v6_actual)) def run_start_logging(self, ipt_mgr, log_info, **kwargs): # Run start logging function with a give log_info router_info = kwargs.get('router_info') log_resources = kwargs.get('log_resources') self._config_default_chains_v4v6(ipt_mgr) if router_info: with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', return_value=log_info): self.log_driver.start_logging(self.context, router_info=router_info) elif log_resources: with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_log_resources', return_value=log_info): self.log_driver.start_logging(self.context, log_resources=log_resources) class FWLoggingTestCase(FWLoggingTestBase): def test_start_logging_when_l3_starting(self): # Get router information ipt_mgr = self.router_info.iptables_manager port_ids = [port['id'] for port in self.router_info.internal_ports] for event in log_const.LOG_EVENTS: # Test start_logging with single log resource f_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] self.run_start_logging(ipt_mgr, log_info=f_log_info, router_info=self.router_info) # Test start_logging with multiple log resources f_log_info = [ self._fake_log_info(log_id='fake_log_id_1', port_ids=[port_ids[0]], event=event), self._fake_log_info(log_id='fake_log_id_2', port_ids=[port_ids[1]], event=event) ] self.run_start_logging(ipt_mgr, log_info=f_log_info, router_info=self.router_info) self.assert_logging_results(ipt_mgr, f_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_start_logging_when_create_log(self): # Get router information ipt_mgr = self.router_info.iptables_manager port_ids = [port['id'] for port in self.router_info.internal_ports] for event in log_const.LOG_EVENTS: log_resources = [self._fake_log_resource(FAKE_PROJECT_ID, event=event)] f_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] self.run_start_logging(ipt_mgr, log_info=f_log_info, log_resources=log_resources) self.assert_logging_results(ipt_mgr, f_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_start_logging_when_add_router_port(self): ipt_mgr = self.router_info.iptables_manager for event in log_const.LOG_EVENTS: port_ids = [port['id'] for port in self.router_info.internal_ports] # Making log_info when there is only one port added_port_id = port_ids.pop() initial_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] # Make log_info with new adding port port_ids.append(added_port_id) add_port_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] self._config_default_chains_v4v6(ipt_mgr) with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', side_effect=[initial_log_info, add_port_log_info]): # Start logging with a single port as normal to get initial # NFLOG rules into iptables self.log_driver.start_logging(self.context, router_info=self.router_info) # Start logging with the new port self.log_driver.start_logging(self.context, router_info=self.router_info) self.assert_logging_results(ipt_mgr, add_port_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_start_logging_when_remove_port(self): ipt_mgr = self.router_info.iptables_manager for event in log_const.LOG_EVENTS: port_ids = [port['id'] for port in self.router_info.internal_ports] # Making log_info when there are two ports on router initial_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] # Make log_info when a port is removed from router port_ids.pop() remove_port_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] self._config_default_chains_v4v6(ipt_mgr) with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', side_effect=[initial_log_info, remove_port_log_info]): # Start logging with a single port as normal to get initial # NFLOG rules into iptables self.log_driver.start_logging(self.context, router_info=self.router_info) # Start logging with the new port self.log_driver.start_logging(self.context, router_info=self.router_info) self.assert_logging_results(ipt_mgr, remove_port_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_start_logging_when_attach_port_to_fwg(self): ipt_mgr = self.router_info.iptables_manager for event in log_const.LOG_EVENTS: port_ids = [port['id'] for port in self.router_info.internal_ports] # Making log_info when there is only one port attached_port_id = port_ids.pop() initial_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] # Make log_info with a new port that attached to fwg port_ids.append(attached_port_id) attached_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] log_resources = [ self._fake_log_resource(FAKE_PROJECT_ID, resource_id=attached_port_id, event=event) ] self._config_default_chains_v4v6(ipt_mgr) with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', return_value=initial_log_info): with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_log_resources', return_value=attached_log_info): # Start logging with a single port as normal to get initial # NFLOG rules into iptables self.log_driver.start_logging(self.context, router_info=self.router_info) # Start logging with the new port attach to fwg self.log_driver.start_logging(self.context, log_resources=log_resources) self.assert_logging_results(ipt_mgr, attached_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_start_logging_when_detach_port_from_fwg(self): ipt_mgr = self.router_info.iptables_manager for event in log_const.LOG_EVENTS: port_ids = [port['id'] for port in self.router_info.internal_ports] # Making log_info when there are two ports attached to fwg initial_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] # Make log_info when a port is detached from fwg detached_port_id = port_ids.pop() detached_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=event) ] log_resources = [ self._fake_log_resource(FAKE_PROJECT_ID, resource_id=detached_port_id, event=event) ] self._config_default_chains_v4v6(ipt_mgr) with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', return_value=initial_log_info): with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_log_resources', return_value=detached_log_info): # Start logging with a single port as normal to get initial # NFLOG rules into iptables self.log_driver.start_logging(self.context, router_info=self.router_info) # Start logging with the new port attach to fwg self.log_driver.start_logging(self.context, log_resources=log_resources) self.assert_logging_results(ipt_mgr, detached_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_start_logging_when_enable_router(self): ipt_mgr = self.router_info.iptables_manager port_ids = [port['id'] for port in self.router_info.internal_ports] for event in log_const.LOG_EVENTS: # Log info to initialize NFLOG rules f_log_info = [ self._fake_log_info(log_id=FAKE_LOG_ID, port_ids=port_ids, event=ALL) ] # Initialize NFLOG rules with start_logging self.run_start_logging(ipt_mgr, log_info=f_log_info, router_info=self.router_info) # Fake disable router by running stop_logging with router_info self.log_driver.stop_logging( self.context, router_info=self.router_info.router_id) # Fake enable router by running start_logging with router_info self.log_driver.start_logging(self.context, router_info=self.router_info) self.assert_logging_results(ipt_mgr, f_log_info) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_stop_logging_when_delete_log(self): ipt_mgr = self.router_info.iptables_manager for event in log_const.LOG_EVENTS: port_ids = [port['id'] for port in self.router_info.internal_ports] # Initialize log_info to start logging log_info_1 = self._fake_log_info(log_id='fake_log_id_1', port_ids=port_ids, event=event) log_info_2 = self._fake_log_info(log_id='fake_log_id_2', port_ids=[port_ids[0]], event=event) initial_log_info = [ log_info_1, log_info_2 ] self._config_default_chains_v4v6(ipt_mgr) with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', return_value=initial_log_info): # Start logging to get initial NFLOG rules self.log_driver.start_logging(self.context, router_info=self.router_info) # Stop logging by deleting fake_log_id_1 deleted_log_1 = [{'id': 'fake_log_id_1'}] self.log_driver.stop_logging(self.context, log_resources=deleted_log_1) self.assert_logging_results(ipt_mgr, [log_info_2]) # Stop logging by deleting fake_log_id_2 deleted_log_2 = [{'id': 'fake_log_id_2'}] self.log_driver.stop_logging(self.context, log_resources=deleted_log_2) self.assert_logging_results(ipt_mgr, []) self._refresh_logging_config(ipt_mgr=ipt_mgr) def test_stop_logging_when_delete_log_with_event_combination(self): ipt_mgr = self.router_info.iptables_manager port_ids = [port['id'] for port in self.router_info.internal_ports] # Initial log_info to start logging log_info_1 = self._fake_log_info(log_id='accept_log_id', port_ids=port_ids, event=ACCEPT) log_info_2 = self._fake_log_info(log_id='all_log_id', port_ids=[port_ids[0]], event=ALL) initial_log_info = [ log_info_1, log_info_2 ] self._config_default_chains_v4v6(ipt_mgr) with mock.patch.object(self.resource_rpc, 'get_sg_log_info_for_port', return_value=initial_log_info): # Start logging to get initial NFLOG rules self.log_driver.start_logging(self.context, router_info=self.router_info) # Stop logging by deleting accept_log_id accepted_log = [{'id': 'accept_log_id'}] self.log_driver.stop_logging(self.context, log_resources=accepted_log) self.assert_logging_results(ipt_mgr, [log_info_2]) # Stop logging by deleting all_log_id all_log = [{'id': 'all_log_id'}] self.log_driver.stop_logging(self.context, log_resources=all_log) self.assert_logging_results(ipt_mgr, []) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/0000775000175000017500000000000000000000000021401 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/__init__.py0000664000175000017500000000000000000000000023500 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/cmd/0000775000175000017500000000000000000000000022144 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/cmd/__init__.py0000664000175000017500000000000000000000000024243 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/cmd/upgrade_checks/0000775000175000017500000000000000000000000025113 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/cmd/upgrade_checks/__init__.py0000664000175000017500000000000000000000000027212 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/cmd/upgrade_checks/test_checks.py0000664000175000017500000000324500000000000027770 0ustar00zuulzuul00000000000000# Copyright 2019 Red Hat Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 oslo_config import cfg from oslo_upgradecheck.upgradecheck import Code from neutron_fwaas.cmd.upgrade_checks import checks from neutron_fwaas.tests import base class TestChecks(base.BaseTestCase): def setUp(self): super(TestChecks, self).setUp() self.checks = checks.Checks() def test_get_checks_list(self): self.assertIsInstance(self.checks.get_checks(), list) def test_fwaas_v1_check_sucess(self): cfg.CONF.set_override('service_plugins', ['l3', 'qos']) check_result = checks.Checks.fwaas_v1_check(mock.Mock()) self.assertEqual(Code.SUCCESS, check_result.code) def test_fwaas_v1_check_warning(self): plugins_to_check = [ ['l3', 'firewall', 'qos'], ['l3', 'neutron_fwaas.services.firewall.fwaas_plugin:FirewallPlugin', 'qos']] for plugins in plugins_to_check: cfg.CONF.set_override('service_plugins', plugins) check_result = checks.Checks.fwaas_v1_check(mock.Mock()) self.assertEqual(Code.FAILURE, check_result.code) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/0000775000175000017500000000000000000000000021766 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/__init__.py0000664000175000017500000000000000000000000024065 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/firewall/0000775000175000017500000000000000000000000023573 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/firewall/__init__.py0000664000175000017500000000000000000000000025672 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/firewall/v2/0000775000175000017500000000000000000000000024122 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/firewall/v2/__init__.py0000664000175000017500000000000000000000000026221 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py0000664000175000017500000026606300000000000030431 0ustar00zuulzuul00000000000000# Copyright (c) 2016 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. from unittest import mock import testtools import webob.exc from neutron_lib import constants as nl_constants from neutron_lib.exceptions import firewall_v2 as f_exc from oslo_config import cfg from oslo_utils import uuidutils from neutron_fwaas.common import fwaas_constants as constants from neutron_fwaas.tests.unit.services.firewall import test_fwaas_plugin_v2 class TestFirewallDBPluginV2(test_fwaas_plugin_v2.FirewallPluginV2TestCase): def setUp(self): super(TestFirewallDBPluginV2, self).setUp() self.db = self.plugin.driver.firewall_db def test_get_policy_ordered_rules(self): with self.firewall_rule(name='alone', as_admin=True), \ self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2: fwrs = fwr1, fwr2, fwr3 expected_ids = [fwr['firewall_rule']['id'] for fwr in fwrs] with self.firewall_policy(firewall_rules=expected_ids, as_admin=True) as fwp: ctx = self._get_admin_context() fwp_id = fwp['firewall_policy']['id'] observeds = self.db._get_policy_ordered_rules(ctx, fwp_id) observed_ids = [r['id'] for r in observeds] self.assertEqual(expected_ids, observed_ids) def test_create_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_policy(name=name, shared=self.SHARED, firewall_rules=None, audited=self.AUDITED, as_admin=True, ) as firewall_policy: for k, v in attrs.items(): self.assertEqual(v, firewall_policy['firewall_policy'][k]) def test_create_firewall_policy_with_rules(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3: fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids with self.firewall_policy(name=name, shared=self.SHARED, firewall_rules=fw_rule_ids, audited=self.AUDITED, as_admin=True) as fwp: for k, v in attrs.items(): self.assertEqual(v, fwp['firewall_policy'][k]) def test_create_firewall_policy_with_previously_associated_rule(self): with self.firewall_rule(as_admin=True) as fwr: fw_rule_ids = [fwr['firewall_rule']['id']] with self.firewall_policy(firewall_rules=fw_rule_ids, as_admin=True): with self.firewall_policy(shared=self.SHARED, firewall_rules=fw_rule_ids, as_admin=True) as fwp2: self.assertEqual( fwr['firewall_rule']['id'], fwp2['firewall_policy']['firewall_rules'][0]) def test_show_firewall_policy(self): name = "firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name) with self.firewall_policy(name=name, shared=self.SHARED, firewall_rules=None, audited=self.AUDITED, as_admin=True) as fwp: res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) for k, v in attrs.items(): self.assertEqual(v, res['firewall_policy'][k]) def test_list_firewall_policies(self): with self.firewall_policy(name='fwp1', description='fwp', as_admin=True) as fwp1, \ self.firewall_policy(name='fwp2', description='fwp', as_admin=True) as fwp2, \ self.firewall_policy(name='fwp3', description='fwp', as_admin=True) as fwp3: fw_policies = [fwp1, fwp2, fwp3] self._test_list_resources('firewall_policy', fw_policies, query_params='description=fwp') def test_update_firewall_policy(self): name = "new_firewall_policy1" attrs = self._get_test_firewall_policy_attrs(name, audited=False) with self.firewall_policy(shared=self.SHARED, firewall_rules=None, audited=self.AUDITED, as_admin=True) as fwp: data = {'firewall_policy': {'name': name}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_policy'][k]) def _test_update_firewall_policy(self, with_audited): with self.firewall_policy(name='firewall_policy1', description='fwp', audited=self.AUDITED, as_admin=True) as fwp: attrs = self._get_test_firewall_policy_attrs(audited=with_audited) data = {'firewall_policy': {'description': 'fw_p1'}} if with_audited: data['firewall_policy']['audited'] = 'True' req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['description'] = 'fw_p1' for k, v in attrs.items(): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_set_audited_false(self): self._test_update_firewall_policy(with_audited=False) def test_update_firewall_policy_with_audited_set_true(self): self._test_update_firewall_policy(with_audited=True) def test_update_firewall_policy_with_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3: with self.firewall_policy(as_admin=True) as fwp: fr = [fwr1, fwr2, fwr3] fw_rule_ids = [r['firewall_rule']['id'] for r in fr] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['audited'] = False attrs['firewall_rules'] = sorted(attrs['firewall_rules']) # TODO(sridar): set it so that the ordering is maintained res['firewall_policy']['firewall_rules'] = sorted( res['firewall_policy']['firewall_rules']) for k, v in attrs.items(): self.assertEqual(v, res['firewall_policy'][k]) def test_update_firewall_policy_replace_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3, \ self.firewall_rule(name='fwr4', as_admin=True) as fwr4: frs = [fwr1, fwr2, fwr3, fwr4] fr1 = frs[0:2] fr2 = frs[2:4] with self.firewall_policy(as_admin=True) as fwp: fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) fw_rule_ids = [r['firewall_rule']['id'] for r in fr2] attrs['firewall_rules'] = fw_rule_ids new_data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', new_data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) attrs['audited'] = False for k, v in attrs.items(): self.assertEqual(v, res['firewall_policy'][k]) @testtools.skip('bug/1614673') def test_update_firewall_policy_reorder_rules(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1') as fwr1, \ self.firewall_rule(name='fwr2') as fwr2, \ self.firewall_rule(name='fwr3') as fwr3, \ self.firewall_rule(name='fwr4') as fwr4: fr = [fwr1, fwr2, fwr3, fwr4] with self.firewall_policy() as fwp: fw_rule_ids = [fr[2]['firewall_rule']['id'], fr[3]['firewall_rule']['id']] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) # shuffle the rules, add more rules fw_rule_ids = [fr[1]['firewall_rule']['id'], fr[3]['firewall_rule']['id'], fr[2]['firewall_rule']['id'], fr[0]['firewall_rule']['id']] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) rules = [] for rule_id in fw_rule_ids: res = self._show_req('firewall_rules', rule_id) rules.append(res['firewall_rule']) self.assertEqual(1, rules[0]['position']) self.assertEqual(fr[1]['firewall_rule']['id'], rules[0]['id']) self.assertEqual(2, rules[1]['position']) self.assertEqual(fr[3]['firewall_rule']['id'], rules[1]['id']) self.assertEqual(3, rules[2]['position']) self.assertEqual(fr[2]['firewall_rule']['id'], rules[2]['id']) self.assertEqual(4, rules[3]['position']) self.assertEqual(fr[0]['firewall_rule']['id'], rules[3]['id']) def test_update_firewall_policy_with_non_existing_rule(self): attrs = self._get_test_firewall_policy_attrs() with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2: fr = [fwr1, fwr2] with self.firewall_policy(as_admin=True) as fwp: fw_rule_ids = [r['firewall_rule']['id'] for r in fr] # appending non-existent rule fw_rule_ids.append(uuidutils.generate_uuid()) data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) res = req.get_response(self.ext_api) # check that the firewall_rule was not found self.assertEqual(404, res.status_int) # check if none of the rules got added to the policy res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) for k, v in attrs.items(): self.assertEqual(v, res['firewall_policy'][k]) def test_update_shared_firewall_policy_with_nonshared_rule(self): with self.firewall_rule(name='fwr1', shared=False, as_admin=True) as fr: with self.firewall_policy(as_admin=True) as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with nonshared rule data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_update_firewall_policy_with_shared_attr_nonshared_rule(self): with self.firewall_rule(name='fwr1', shared=False) as fr: with self.firewall_policy(shared=False) as fwp: fw_rule_ids = [fr['firewall_rule']['id']] # update shared policy with shared attr and nonshared rule data = {'firewall_policy': {'shared': self.SHARED, 'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_update_firewall_policy_with_shared_attr_exist_unshared_rule(self): with self.firewall_rule(name='fwr1', shared=False, as_admin=True) as fwr: fwr_ids = [fwr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fwr_ids, as_admin=True) as fwp: # Update policy with shared attr data = {'firewall_policy': {'shared': self.SHARED}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_with_shared_and_shared_rules(self): with self.firewall_rule(name='fwr1', shared=self.SHARED, as_admin=True) as fwr: fwr_ids = [fwr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fwr_ids, as_admin=True) as fwp: # Update policy with shared attr data = {'firewall_policy': {'shared': self.SHARED}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def test_update_firewall_policy_assoc_with_other_tenant_firewall(self): with self.firewall_policy(shared=self.SHARED, tenant_id='tenant1', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, as_admin=True): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_policy_from_shared_to_unshared(self): with self.firewall_policy(shared=True, as_admin=True) as fwp: # update policy with public attr data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPOk.code, res.status_int) def test_update_from_shared_to_unshared_associated_as_ingress_fwp(self): with self.firewall_policy(shared=True, tenant_id='here', as_admin=True) as fwp: # update policy with public attr fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(tenant_id='another', ingress_firewall_policy_id=fwp_id, as_admin=True): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_from_shared_to_unshared_associated_as_egress_fwp(self): with self.firewall_policy(shared=True, tenant_id='here', as_admin=True) as fwp: # update policy with public attr fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(tenant_id='another', egress_firewall_policy_id=fwp_id, as_admin=True): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_from_shared_to_unshared_associated_as_ingress_egress(self): with self.firewall_policy(shared=True, tenant_id='here', as_admin=True) as fwp: # update policy with public attr fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(tenant_id='another', egress_firewall_policy_id=fwp_id, ingress_firewall_policy_id=fwp_id, as_admin=True): data = {'firewall_policy': {'shared': False}} req = self.new_update_request('firewall_policies', data, fwp_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_default_fwg_policy(self): """Test that fw group associated policy can't be updated Make sure that neither admin nor non-admin can update policy associated with default firewall group """ ctx_admin = self._get_admin_context() ctx_nonadmin = self._get_nonadmin_context() for ctx, as_admin in [(ctx_admin, True), (ctx_nonadmin, False)]: self._build_default_fwg(ctx=ctx, as_admin=as_admin) policies = self._list_req('firewall_policies') for p in policies: data = {'firewall_policy': {'firewall_rules': []}} req = self.new_update_request('firewall_policies', data, p['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_delete_firewall_policy(self): ctx = self._get_admin_context() with self.firewall_policy(do_delete=False, as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] req = self.new_delete_request('firewall_policies', fwp_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallPolicyNotFound, self.plugin.get_firewall_policy, ctx, fwp_id) @testtools.skip('bug/1614673') def test_delete_firewall_policy_with_rule(self): ctx = self._get_admin_context() attrs = self._get_test_firewall_policy_attrs() with self.firewall_policy(do_delete=False) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_rule(name='fwr1') as fr: fr_id = fr['firewall_rule']['id'] fw_rule_ids = [fr_id] attrs['firewall_rules'] = fw_rule_ids data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) fw_rule = self.plugin.get_firewall_rule(ctx, fr_id) self.assertEqual(fwp_id, fw_rule['ingress_firewall_policy_id']) req = self.new_delete_request('firewall_policies', fwp_id) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallPolicyNotFound, self.plugin.get_firewall_policy, ctx, fwp_id) fw_rule = self.plugin.get_firewall_rule(ctx, fr_id) self.assertIsNone(fw_rule['ingress_firewall_policy_id']) def test_delete_firewall_policy_with_firewall_group_association(self): with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP, as_admin=True): req = self.new_delete_request('firewall_policies', fwp_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_create_firewall_rule(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule(as_admin=True) as firewall_rule: for k, v in attrs.items(): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(source_port=None, destination_port=None, as_admin=True) as firewall_rule: for k, v in attrs.items(): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(source_port=10000, destination_port=80, as_admin=True) as firewall_rule: for k, v in attrs.items(): self.assertEqual(v, firewall_rule['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(source_port='10000', destination_port='80', as_admin=True) as firewall_rule: for k, v in attrs.items(): self.assertEqual(v, firewall_rule['firewall_rule'][k]) def test_create_firewall_src_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_port'] = '65535:1024' attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_dest_port_illegal_range(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_port'] = '65535:1024' attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, **attrs) def test_create_firewall_rule_icmp_with_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) def test_create_firewall_rule_icmp_without_port(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = 'icmp' attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(source_port=None, destination_port=None, protocol='icmp', as_admin=True) as firewall_rule: for k, v in attrs.items(): self.assertEqual(v, firewall_rule['firewall_rule'][k]) def test_create_firewall_without_source(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None attrs['expected_res_status'] = 201 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) def test_create_firewall_rule_without_destination(self): attrs = self._get_test_firewall_rule_attrs() attrs['destination_ip_address'] = None attrs['expected_res_status'] = 201 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) def test_create_firewall_rule_without_protocol_with_dport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['source_port'] = None attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) def test_create_firewall_rule_without_protocol_with_sport(self): attrs = self._get_test_firewall_rule_attrs() attrs['protocol'] = None attrs['destination_port'] = None attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) def test_show_firewall_rule_with_fw_policy_not_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule(as_admin=True) as fw_rule: res = self._show_req('firewall_rules', fw_rule['firewall_rule']['id']) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) @testtools.skip('bug/1614673') def test_show_firewall_rule_with_fw_policy_associated(self): attrs = self._get_test_firewall_rule_attrs() with self.firewall_rule() as fw_rule: with self.firewall_policy() as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id data = {'firewall_policy': {'firewall_rules': [fw_rule['firewall_rule']['id']]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id']) req.get_response(self.ext_api) res = self._show_req('firewall_rules', fw_rule['firewall_rule']['id']) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) def test_create_firewall_rule_with_ipv6_addrs_and_wrong_ip_version(self): attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = None attrs['destination_ip_address'] = '2001:db8:3::/64' attrs['ip_version'] = 4 attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) attrs = self._get_test_firewall_rule_attrs() attrs['source_ip_address'] = '::/0' attrs['destination_ip_address'] = None attrs['ip_version'] = 4 attrs['expected_res_status'] = 400 self._create_firewall_rule(self.fmt, as_admin=True, **attrs) def test_list_firewall_rules(self): with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3: fr = [fwr1, fwr2, fwr3] query_params = 'protocol=tcp' self._test_list_resources('firewall_rule', fr, query_params=query_params) def test_update_firewall_rule(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) attrs['source_port'] = '10:20' attrs['destination_port'] = '30:40' with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'name': name, 'protocol': self.PROTOCOL, 'source_port': '10:20', 'destination_port': '30:40'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'name': name, 'protocol': self.PROTOCOL, 'source_port': 10000, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = '10000' attrs['destination_port'] = '80' with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'name': name, 'protocol': self.PROTOCOL, 'source_port': '10000', 'destination_port': '80'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) attrs['source_port'] = None attrs['destination_port'] = None with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'name': name, 'source_port': None, 'destination_port': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) def test_update_firewall_rule_with_port_and_no_proto(self): with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'protocol': None, 'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_without_ports_and_no_proto(self): with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'protocol': None, 'destination_port': None, 'source_port': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_rule_with_port(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None, as_admin=True) as fwr: data = {'firewall_rule': {'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_port_illegal_range(self): with self.firewall_rule(as_admin=True) as fwr: data = {'firewall_rule': {'destination_port': '65535:1024'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_with_port_and_protocol(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None, as_admin=True) as fwr: data = {'firewall_rule': {'destination_port': 80, 'protocol': 'tcp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_rule_icmp_with_port(self): with self.firewall_rule(source_port=None, destination_port=None, protocol=None, as_admin=True) as fwr: data = {'firewall_rule': {'destination_port': 80, 'protocol': 'icmp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) with self.firewall_rule(source_port=None, destination_port=None, protocol='icmp', as_admin=True) as fwr: data = {'firewall_rule': {'destination_port': 80}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(400, res.status_int) def test_update_firewall_rule_protocol_icmp(self): with self.firewall_rule(source_port=10000, as_admin=True) as fwr: data = {'firewall_rule': {'protocol': 'icmp'}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_firewall_rule_protocol_none(self): with self.firewall_rule(source_port=10000, as_admin=True) as fwr: data = {'firewall_rule': {'protocol': None}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPBadRequest.code, res.status_int) def test_update_firewall_rule_with_policy_associated(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) with self.firewall_rule(as_admin=True) as fwr: with self.firewall_policy(as_admin=True) as fwp: fwr_id = fwr['firewall_rule']['id'] data = {'firewall_policy': {'firewall_rules': [fwr_id]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) req.get_response(self.ext_api) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id'], as_admin=True) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) res = self._show_req('firewall_policies', fwp['firewall_policy']['id']) self.assertEqual( [fwr_id], res['firewall_policy']['firewall_rules']) self.assertFalse(res['firewall_policy']['audited']) @testtools.skip('bug/1614680') def test_update_firewall_rule_associated_with_other_tenant_policy(self): with self.firewall_rule(shared=self, tenant_id='tenant1') as fwr: fwr_id = [fwr['firewall_rule']['id']] with self.firewall_policy(shared=False, firewall_rules=fwr_id): data = {'firewall_rule': {'shared': False}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_update_firewall_rule_with_ipv6_ipaddr(self): with self.firewall_rule(source_ip_address="1::10", destination_ip_address=None, ip_version=6, as_admin=True) as fwr_v6: data = {'firewall_rule': { 'destination_ip_address': "2::20"}} req = self.new_update_request('firewall_rules', data, fwr_v6['firewall_rule']['id'], as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_delete_firewall_rule(self): ctx = self._get_admin_context() with self.firewall_rule(do_delete=False, as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] req = self.new_delete_request('firewall_rules', fwr_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallRuleNotFound, self.plugin.get_firewall_rule, ctx, fwr_id) def test_delete_firewall_rule_with_policy_associated(self): with self.firewall_rule(as_admin=True) as fwr: with self.firewall_policy(as_admin=True) as fwp: fwr_id = fwr['firewall_rule']['id'] data = {'firewall_policy': {'firewall_rules': [fwr_id]}} req = self.new_update_request('firewall_policies', data, fwp['firewall_policy']['id'], as_admin=True) req.get_response(self.ext_api) req = self.new_delete_request('firewall_rules', fwr_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_create_firewall_group(self): attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) def test_create_firewall_group_with_router_port(self): with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as port: attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [port['port']['id']] self._test_create_firewall_group(attrs) def test_create_firewall_group_with_dvr_port(self): with self.port( device_owner=nl_constants.DEVICE_OWNER_DVR_INTERFACE) as port: attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [port['port']['id']] self._test_create_firewall_group(attrs) def test_create_firewall_group_with_router_port_l3ha(self): dev_owner_ha_repl_int = nl_constants.DEVICE_OWNER_HA_REPLICATED_INT with self.port(device_owner=dev_owner_ha_repl_int) as port: attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [port['port']['id']] self._test_create_firewall_group(attrs) def test_create_firewall_group_with_empty_ports(self): attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [] self._test_create_firewall_group(attrs) def test_create_default_firewall_group_multiple_times_diff_tenants(self): ctx_admin = self._get_admin_context() fwg_admin = self._build_default_fwg(ctx=ctx_admin, as_admin=True) res = self._build_default_fwg(ctx=ctx_admin, is_one=False, as_admin=True) # check that only 1 group has been created self.assertEqual(1, len(res)) ctx = self._get_nonadmin_context() fwg_na = self._build_default_fwg(ctx=ctx) res = self._build_default_fwg(ctx=ctx, is_one=False) # check that only 1 group has been created self.assertEqual(1, len(res)) # make sure that admin default_fwg and non_admin don't match self.assertNotEqual(fwg_na['id'], fwg_admin['id']) # make sure that admin can see default groups for admin and non-admin res = self._list_req('firewall_groups', ctx=ctx_admin, as_admin=True) self.assertEqual(2, len(res)) self.assertEqual(set([ctx_admin.tenant_id, ctx.tenant_id]), set([r['tenant_id'] for r in res])) def test_create_default_firewall_group_from_config(self): group = 'default_fwg_rules' cfg.CONF.set_override('shared', True, group) cfg.CONF.set_override('protocol', 'tcp', group) cfg.CONF.set_override('enabled', False, group) cfg.CONF.set_override('ingress_action', 'allow', group) cfg.CONF.set_override('egress_action', 'deny', group) cfg.CONF.set_override('ingress_source_port', '7777', group) cfg.CONF.set_override('egress_source_port', '8888', group) cfg.CONF.set_override('ingress_destination_port', '6666', group) cfg.CONF.set_override('egress_destination_port', '5555', group) cfg.CONF.set_override('ingress_source_ipv4_address', '1.2.3.4', group) cfg.CONF.set_override('ingress_source_ipv6_address', '1:2:3:4:5:6:7:8', group) cfg.CONF.set_override('egress_source_ipv4_address', '4.3.2.1', group) cfg.CONF.set_override('egress_source_ipv6_address', '8:7:6:5:4:3:2:1', group) cfg.CONF.set_override('ingress_destination_ipv4_address', '251.252.253.254', group) cfg.CONF.set_override('ingress_destination_ipv6_address', '88:99:aa:bb:cc:dd:ee:ff', group) cfg.CONF.set_override('egress_destination_ipv4_address', '255.254.253.252', group) cfg.CONF.set_override('egress_destination_ipv6_address', 'ff:ee:dd:cc:bb:aa:99:88', group) self._build_default_fwg() results = self._list_req('firewall_rules') for res in results: res.pop('id') base = { 'shared': True, 'protocol': 'tcp', 'enabled': False, 'tenant_id': 'admin-tenant', 'project_id': 'admin-tenant', 'firewall_policy_id': None } ingress_base = dict(base, **{ 'source_port': '7777', 'destination_port': '6666', 'action': 'allow' }) egress_base = dict(base, **{ 'source_port': '8888', 'destination_port': '5555', 'action': 'deny' }) expected = [dict(ingress_base, **{ 'name': 'default ingress ipv4', 'description': 'default ingress rule for IPv4', 'ip_version': 4, 'source_ip_address': '1.2.3.4', 'destination_ip_address': '251.252.253.254', }), dict(ingress_base, **{ 'name': 'default ingress ipv6', 'description': 'default ingress rule for IPv6', 'ip_version': 6, 'source_ip_address': '1:2:3:4:5:6:7:8', 'destination_ip_address': '88:99:aa:bb:cc:dd:ee:ff', }), dict(egress_base, **{ 'name': 'default egress ipv4', 'description': 'default egress rule for IPv4', 'ip_version': 4, 'source_ip_address': '4.3.2.1', 'destination_ip_address': '255.254.253.252', }), dict(egress_base, **{ 'name': 'default egress ipv6', 'description': 'default egress rule for IPv6', 'ip_version': 6, 'source_ip_address': '8:7:6:5:4:3:2:1', 'destination_ip_address': 'ff:ee:dd:cc:bb:aa:99:88', })] self.assertEqual(expected, results) def test_create_default_firewall_group(self): self._build_default_fwg() result_map = { 'firewall_groups': {"keys": ["description", "name"], "data": [("Default firewall group", constants.DEFAULT_FWG)] }, 'firewall_policies': { "keys": ["description", "name"], "data": [("Ingress firewall policy", constants.DEFAULT_FWP_INGRESS), ("Egress firewall policy", constants.DEFAULT_FWP_EGRESS)]}, 'firewall_rules': { "keys": ["description", "action", "protocol", "enabled", "ip_version", "name"], "data": [ ("default ingress rule for IPv4", "deny", None, True, 4, "default ingress ipv4"), ("default egress rule for IPv4", "allow", None, True, 4, "default egress ipv4"), ("default ingress rule for IPv6", "deny", None, True, 6, "default ingress ipv6"), ("default egress rule for IPv6", "allow", None, True, 6, "default egress ipv6")] } } def _check_rules_match_policies(policy, direction): if direction in policy["description"].lower(): for rule_id in policy['firewall_rules']: rule = self._show_req( 'firewall_rules', rule_id)['firewall_rule'] self.assertIn(direction, rule["description"]) for obj in result_map: res = self._list_req(obj) check_keys = result_map[obj]["keys"] expected = result_map[obj]["data"] self.assertEqual(len(expected), len(res)) # an attempt to check that rules match policies if obj == 'firewall_policies': for p in res: _check_rules_match_policies(p, "ingress") _check_rules_match_policies(p, "egress") # check that a rule with given params is present in actual # data by comparing expected/actual tuples actual = [] for r in res: actual.append(tuple(r[key] for key in check_keys)) self.assertEqual(set(expected), set(actual)) def test_create_firewall_group_exists_default(self): self._build_default_fwg()['id'] attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) def test_create_firewall_group_with_fwp_does_not_exist(self): fmt = self.fmt fwg_name = "firewall1" description = "my_firewall1" not_found_fwp_id = uuidutils.generate_uuid() self._create_firewall_group(fmt, fwg_name, description, not_found_fwp_id, not_found_fwp_id, ports=None, admin_state_up=self.ADMIN_STATE_UP, expected_res_status=404) def test_create_firewall_group_with_fwp_on_different_tenant(self): fmt = self.fmt fwg_name = "firewall1" description = "my_firewall1" with self.firewall_policy(shared=False, tenant_id='tenant2', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = self._get_nonadmin_context() self._create_firewall_group(fmt, fwg_name, description, ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, context=ctx, expected_res_status=404) def test_create_firewall_group_with_admin_and_fwp_different_tenant(self): fmt = self.fmt fwg_name = "firewall1" description = "my_firewall1" with self.firewall_policy(shared=False, tenant_id='tenant2', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = self._get_admin_context() self._create_firewall_group(fmt, fwg_name, description, fwp_id, fwp_id, tenant_id="admin-tenant", context=ctx, expected_res_status=404, as_admin=True) def test_create_firewall_group_with_admin_and_fwp_is_shared(self): fwg_name = "fw_with_shared_fwp" with self.firewall_policy(tenant_id="tenantX", as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] ctx = self._get_admin_context() target_tenant = 'tenant1' with self.firewall_group( name=fwg_name, ingress_firewall_policy_id=fwp_id, tenant_id=target_tenant, context=ctx, admin_state_up=self.ADMIN_STATE_UP, as_admin=True) as fwg: self.assertEqual(target_tenant, fwg['firewall_group']['tenant_id']) def _test_show_firewall_group(self, attrs): with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id attrs['egress_firewall_policy_id'] = fwp_id with self.firewall_group( name=attrs['name'], ports=attrs['ports'] if 'ports' in attrs else None, ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP) as firewall_group: res = self._show_req('firewall_groups', firewall_group['firewall_group']['id']) for k, v in attrs.items(): self.assertEqual(v, res['firewall_group'][k]) def test_show_firewall_group(self): attrs = self._get_test_firewall_group_attrs('fwg1') self._test_show_firewall_group(attrs) def test_show_firewall_group_with_ports(self): attrs = self._get_test_firewall_group_attrs('fwg1') dev_owner_router_intf = nl_constants.DEVICE_OWNER_ROUTER_INTF with self.port(device_owner=dev_owner_router_intf) as dummy_port: attrs['ports'] = [dummy_port['port']['id']] self._test_show_firewall_group(attrs) def test_show_firewall_group_with_empty_ports(self): attrs = self._get_test_firewall_group_attrs('fwg1') attrs['ports'] = [] self._test_show_firewall_group(attrs) def test_list_firewall_groups(self): with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='fwg1', tenant_id='tenant1', ingress_firewall_policy_id=fwp_id, description='fwg', as_admin=True) as fwg1, \ self.firewall_group( name='fwg2', tenant_id='tenant2', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, description='fwg', as_admin=True) as fwg2, \ self.firewall_group( name='fwg3', tenant_id='tenant3', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, description='fwg', as_admin=True) as fwg3: fwgrps = [fwg1, fwg2, fwg3] self._test_list_resources('firewall_group', fwgrps, query_params='description=fwg', as_admin=True) def test_update_firewall_group(self): name = "new_firewall1" attrs = self._get_test_firewall_group_attrs(name) with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP, as_admin=True) as firewall: data = {'firewall_group': {'name': name}} req = self.new_update_request('firewall_groups', data, firewall['firewall_group']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_group'][k]) def test_existing_default_create_default_firewall_group(self): self._build_default_fwg(as_admin=True) self._create_firewall_group(fmt=None, name=constants.DEFAULT_FWG, description="", ingress_firewall_policy_id=None, egress_firewall_policy_id=None, expected_res_status=409, as_admin=True) def test_update_default_firewall_group_with_non_admin_success(self): ctx = self._get_nonadmin_context() def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] success_cases = [ {'ports': [port_id]}, {'ports': []}, {'ports': None}, {}, ] for attr in success_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_default_firewall_group_with_non_admin_failure(self): ctx = self._get_nonadmin_context() def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] conflict_cases = [ {'name': ''}, {'name': 'default'}, {'name': 'non-default'}, {'ingress_firewall_policy_id': None}, {'egress_firewall_policy_id': None}, {'description': 'try to modify'}, {'admin_state_up': True}, {'ports': [port_id], 'name': ''}, {'ports': [], 'name': 'default'}, {'ports': None, 'name': 'non-default'}, ] for attr in conflict_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_default_firewall_group_with_admin_success(self): ctx = self._get_admin_context() with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] success_cases = [ {'ports': [port_id]}, {'ports': []}, {'ports': None}, {'ingress_firewall_policy_id': None}, {'egress_firewall_policy_id': None}, {'description': 'try to modify'}, {'admin_state_up': True}, {}, ] for attr in success_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_default_firewall_group_with_admin_failure(self): ctx = self._get_admin_context() with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF, tenant_id=ctx.project_id) as dummy_port: port_id = dummy_port['port']['id'] def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] conflict_cases = [ {'name': 'default'}, {'name': 'non-default'}, {'name': ''}, {'ports': [port_id], 'name': ''}, {'ports': [], 'name': 'default'}, {'ports': None, 'name': 'non-default'}, ] for attr in conflict_cases: data = {'firewall_group': attr} req = self.new_update_request( 'firewall_groups', data, def_fwg_id) req.environ['neutron.context'] = ctx res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_firewall_group_with_fwp(self): ctx = self._get_nonadmin_context() with self.firewall_policy(name='p1', tenant_id=ctx.tenant_id, shared=True, as_admin=True) as fwp1, \ self.firewall_policy(name='p2', tenant_id=ctx.tenant_id, shared=True, as_admin=True) as fwp2, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], egress_firewall_policy_id=fwp2['firewall_policy']['id'], context=ctx, as_admin=True) as fw: fw_id = fw['firewall_group']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall_group': {'ingress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_change_fwg_name_to_default(self): """Test that fw group name can't be changed Make sure that neither admin nor non-admin can change name of existing firewall group to default """ admin_ctx = self._get_admin_context() nonadmin_ctx = self._get_nonadmin_context() with self.firewall_group(context=nonadmin_ctx, as_admin=True) as fwg: data = {'firewall_group': {'name': constants.DEFAULT_FWG}} fwg_id = fwg['firewall_group']['id'] for ctx in [admin_ctx, nonadmin_ctx]: req = self.new_update_request('firewall_groups', data, fwg_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) @testtools.skip('bug/1614680') def test_update_firewall_group_with_shared_fwp(self): ctx = self._get_nonadmin_context() with self.firewall_policy(name='p1', tenant_id=ctx.tenant_id, shared=True) as fwp1, \ self.firewall_policy(name='p2', tenant_id='tenant2', shared=True) as fwp2, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], egress_firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx) as fw: fw_id = fw['firewall_group']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall_group': {'ingress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(200, res.status_int) def test_update_firewall_group_with_admin_and_fwp_different_tenant(self): ctx = self._get_admin_context() with self.firewall_policy(as_admin=True) as fwp1, \ self.firewall_policy(tenant_id='tenant2', shared=False, as_admin=True) as fwp2, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], egress_firewall_policy_id=fwp1['firewall_policy']['id'], context=ctx, as_admin=True) as fw: fw_id = fw['firewall_group']['id'] fwp2_id = fwp2['firewall_policy']['id'] data = {'firewall_group': {'egress_firewall_policy_id': fwp2_id}} req = self.new_update_request('firewall_groups', data, fw_id, context=ctx) res = req.get_response(self.ext_api) self.assertEqual(404, res.status_int) def test_update_firewall_group_fwp_not_found_on_different_tenant(self): with self.firewall_policy(name='fwp1', tenant_id='tenant1', shared=False, do_delete=False, as_admin=True) as fwp1, \ self.firewall_group( ingress_firewall_policy_id=fwp1['firewall_policy']['id'], tenant_id='tenant1', do_delete=False, as_admin=True) as fwg: fwg_id = fwg['firewall_group']['id'] # fw_db = self.db._get_firewall_group(ctx_tenant1, fwg_id) # fw_db['status'] = nl_constants.ACTIVE # update firewall from fwp1 to fwp2 (different tenant) with self.firewall_policy(name='fwp2', tenant_id='tenant2', shared=False, as_admin=True) as fwp2: data = { 'firewall_group': { 'ingress_firewall_policy_id': fwp2['firewall_policy']['id'], }, } req = self.new_update_request('firewall_groups', data, fwg_id, tenant_id='tenant1') res = req.get_response(self.ext_api) self.assertEqual(404, res.status_int) def test_delete_firewall_group(self): ctx = self._get_admin_context() with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group(ingress_firewall_policy_id=fwp_id, do_delete=False, as_admin=True) as fw: fw_id = fw['firewall_group']['id'] req = self.new_delete_request('firewall_groups', fw_id, as_admin=True) res = req.get_response(self.ext_api) self.assertEqual(204, res.status_int) self.assertRaises(f_exc.FirewallGroupNotFound, self.plugin.get_firewall_group, ctx, fw_id) def test_delete_firewall_group_already_deleted(self): ctx = self._get_admin_context() with self.firewall_group(do_delete=False, context=ctx, as_admin=True) as fwg: fwg_id = fwg['firewall_group']['id'] self.assertIsNone(self.plugin.delete_firewall_group(ctx, fwg_id)) # No error raise is fwg not found on delete self.assertIsNone(self.plugin.delete_firewall_group(ctx, fwg_id)) def test_delete_default_firewall_group_with_admin(self): ctx_a = self._get_admin_context() ctx_na = self._get_nonadmin_context() def_fwg_id = None for ctx in [ctx_na, ctx_a]: def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] req = self.new_delete_request('firewall_groups', def_fwg_id) req.environ['neutron.context'] = ctx_a self.assertEqual(204, req.get_response(self.ext_api).status_int) # check that policy has been deleted by listing as admin and getting 1 # default fwg with a differnt id res = self._list_req('firewall_groups', ctx=ctx_a) self.assertEqual(1, len(res)) self.assertNotEqual(def_fwg_id, res[0]['id']) def test_delete_default_firewall_group_with_non_admin(self): ctx = self._get_nonadmin_context() def_fwg_id = self._build_default_fwg(ctx=ctx)['id'] req = self.new_delete_request('firewall_groups', def_fwg_id) req.environ['neutron.context'] = ctx self.assertEqual(409, req.get_response(self.ext_api).status_int) def test_insert_rule_in_policy_with_prior_rules_added_via_update(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3: frs = [fwr1, fwr2, fwr3] fr1 = frs[0:2] fwr3 = frs[2] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] attrs['firewall_rules'] = fw_rule_ids[:] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id) req.get_response(self.ext_api) self._rule_action('insert', fwp_id, fw_rule_ids[0], insert_before=fw_rule_ids[0], insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None) fwr3_id = fwr3['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr3_id) self._rule_action('insert', fwp_id, fwr3_id, insert_before=fw_rule_ids[0], insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) def test_insert_rule_in_policy_failures(self): with self.firewall_rule(name='fwr1', as_admin=True) as fr1: with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] fr1_id = fr1['firewall_rule']['id'] fw_rule_ids = [fr1_id] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id, as_admin=True) req.get_response(self.ext_api) # test inserting with empty request body self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data={}) # test inserting when firewall_rule_id is missing in # request body insert_data = {'insert_before': '123', 'insert_after': '456'} self._rule_action('insert', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data=insert_data) # test inserting when firewall_rule_id is None insert_data = {'firewall_rule_id': None, 'insert_before': '123', 'insert_after': '456'} self._rule_action('insert', fwp_id, None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) # test inserting when firewall_policy_id is incorrect self._rule_action('insert', '123', fr1_id, expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test inserting when firewall_policy_id is None self._rule_action('insert', None, fr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_insert_rule_and_already_associated(self): with self.firewall_rule(as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(firewall_rules=[fwr_id], as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPConflict.code, body_data={'firewall_rule_id': fwr_id}) def test_insert_rule_for_previously_associated_rule(self): with self.firewall_rule(as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] fw_rule_ids = [fwr_id] with self.firewall_policy(firewall_rules=fw_rule_ids, as_admin=True): with self.firewall_policy(name='firewall_policy2', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=insert_data) def test_insert_rule_for_previously_associated_rule_other_tenant(self): with self.firewall_rule(tenant_id='tenant-2', as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] fw_rule_ids = [fwr_id] with self.firewall_policy(tenant_id='tenant-2', firewall_rules=fw_rule_ids, as_admin=True): with self.firewall_policy(name='firewall_policy2', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=insert_data) def test_insert_rule_for_prev_associated_ref_rule(self): with self.firewall_rule(name='fwr0', as_admin=True) as fwr0, \ self.firewall_rule(name='fwr1', as_admin=True) as fwr1: fwr = [fwr0, fwr1] fwr0_id = fwr[0]['firewall_rule']['id'] fwr1_id = fwr[1]['firewall_rule']['id'] with self.firewall_policy(name='fwp0', as_admin=True) as fwp0, \ self.firewall_policy(name='fwp1', firewall_rules=[fwr1_id], as_admin=True) as fwp1: fwp = [fwp0, fwp1] fwp0_id = fwp[0]['firewall_policy']['id'] # test inserting before a rule which # is associated with different policy self._rule_action('insert', fwp0_id, fwr0_id, insert_before=fwr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) # test inserting after a rule which # is associated with different policy self._rule_action('insert', fwp0_id, fwr0_id, insert_after=fwr1_id, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_insert_rule_for_policy_of_other_tenant(self): with self.firewall_rule(shared=False) as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(tenant_id='tenant-2', name='firewall_policy', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': fwr_id} self._rule_action( 'insert', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPConflict.code, expected_body=None, body_data=insert_data, as_admin=True) def test_insert_rule_missing_rule_id(self): with self.firewall_rule(shared=False, as_admin=True): with self.firewall_policy(name='firewall_policy', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {} self._rule_action( 'insert', fwp_id, None, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data=insert_data) def test_insert_rule_empty_rule_id(self): with self.firewall_rule(shared=False, as_admin=True): with self.firewall_policy(name='firewall_policy', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] insert_data = {'firewall_rule_id': None} self._rule_action( 'insert', fwp_id, None, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) def test_insert_rule_invalid_rule_id(self): with self.firewall_rule(shared=False, as_admin=True): with self.firewall_policy(name='firewall_policy', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] fwr_id_fake = 'foo' insert_data = {'firewall_rule_id': fwr_id_fake} self._rule_action( 'insert', fwp_id, fwr_id_fake, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) def test_insert_rule_nonexistent_rule_id(self): with self.firewall_rule(shared=False, as_admin=True): with self.firewall_policy(name='firewall_policy', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] fwr_id_fake = uuidutils.generate_uuid() insert_data = {'firewall_rule_id': fwr_id_fake} self._rule_action( 'insert', fwp_id, fwr_id_fake, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data=insert_data) def test_insert_rule_in_policy(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False with self.firewall_rule(name='fwr0', as_admin=True) as fwr0, \ self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3, \ self.firewall_rule(name='fwr4', as_admin=True) as fwr4, \ self.firewall_rule(name='fwr5', as_admin=True) as fwr5, \ self.firewall_rule(name='fwr6', as_admin=True) as fwr6: fwr = [fwr0, fwr1, fwr2, fwr3, fwr4, fwr5, fwr6] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id # test insert when rule list is empty fwr0_id = fwr[0]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr0_id) self._rule_action('insert', fwp_id, fwr0_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert at top of rule list, insert_before and # insert_after not provided fwr1_id = fwr[1]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr1_id) insert_data = {'firewall_rule_id': fwr1_id} self._rule_action('insert', fwp_id, fwr0_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs, body_data=insert_data) # test insert at top of list above existing rule fwr2_id = fwr[2]['firewall_rule']['id'] attrs['firewall_rules'].insert(0, fwr2_id) self._rule_action('insert', fwp_id, fwr2_id, insert_before=fwr1_id, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert at bottom of list fwr3_id = fwr[3]['firewall_rule']['id'] attrs['firewall_rules'].append(fwr3_id) self._rule_action('insert', fwp_id, fwr3_id, insert_before=None, insert_after=fwr0_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert in the middle of the list using # insert_before fwr4_id = fwr[4]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr4_id) self._rule_action('insert', fwp_id, fwr4_id, insert_before=fwr1_id, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert in the middle of the list using # insert_after fwr5_id = fwr[5]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr5_id) self._rule_action('insert', fwp_id, fwr5_id, insert_before=None, insert_after=fwr2_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) # test insert when both insert_before and # insert_after are set fwr6_id = fwr[6]['firewall_rule']['id'] attrs['firewall_rules'].insert(1, fwr6_id) self._rule_action('insert', fwp_id, fwr6_id, insert_before=fwr5_id, insert_after=fwr5_id, expected_code=webob.exc.HTTPOk.code, expected_body=attrs) def test_remove_rule_and_not_associated(self): with self.firewall_rule(name='fwr0', as_admin=True) as fwr: with self.firewall_policy(name='firewall_policy2', as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] fwr_id = fwr['firewall_rule']['id'] msg = "Firewall rule {0} is not associated with " \ "firewall policy {1}.".format(fwr_id, fwp_id) result = self._rule_action( 'remove', fwp_id, fwr_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPBadRequest.code, body_data={'firewall_rule_id': fwr_id}) self.assertEqual(msg, result['NeutronError']['message']) def test_remove_rule_from_policy(self): attrs = self._get_test_firewall_policy_attrs() attrs['audited'] = False with self.firewall_rule(name='fwr1', as_admin=True) as fwr1, \ self.firewall_rule(name='fwr2', as_admin=True) as fwr2, \ self.firewall_rule(name='fwr3', as_admin=True) as fwr3: fr1 = [fwr1, fwr2, fwr3] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['id'] = fwp_id fw_rule_ids = [r['firewall_rule']['id'] for r in fr1] attrs['firewall_rules'] = fw_rule_ids[:] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id, as_admin=True) req.get_response(self.ext_api) # test removing a rule from a policy that does not exist self._rule_action('remove', '123', fw_rule_ids[1], expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test removing a rule in the middle of the list attrs['firewall_rules'].remove(fw_rule_ids[1]) self._rule_action('remove', fwp_id, fw_rule_ids[1], expected_body=attrs) # test removing a rule at the top of the list attrs['firewall_rules'].remove(fw_rule_ids[0]) self._rule_action('remove', fwp_id, fw_rule_ids[0], expected_body=attrs) # test removing remaining rule in the list attrs['firewall_rules'].remove(fw_rule_ids[2]) self._rule_action('remove', fwp_id, fw_rule_ids[2], expected_body=attrs) # test removing rule that is not associated with the policy self._rule_action('remove', fwp_id, fw_rule_ids[2], expected_code=webob.exc.HTTPBadRequest.code, expected_body=None) def test_remove_rule_from_policy_failures(self): with self.firewall_rule(name='fwr1', as_admin=True) as fr1: with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] fw_rule_ids = [fr1['firewall_rule']['id']] data = {'firewall_policy': {'firewall_rules': fw_rule_ids}} req = self.new_update_request('firewall_policies', data, fwp_id, as_admin=True) req.get_response(self.ext_api) # test removing rule that does not exist self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None) # test removing rule with bad request self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPBadRequest.code, expected_body=None, body_data={}) # test removing rule with firewall_rule_id set to None self._rule_action('remove', fwp_id, '123', expected_code=webob.exc.HTTPNotFound.code, expected_body=None, body_data={'firewall_rule_id': None}) def test_show_firewall_rule_by_name(self): with self.firewall_rule(name='firewall_Rule1', as_admin=True) as fw_rule: res = self._show('firewall_rules', fw_rule['firewall_rule']['id']) self.assertEqual('firewall_Rule1', res['firewall_rule']['name']) def test_show_firewall_policy_by_name(self): with self.firewall_policy(name='firewall_Policy1', as_admin=True) as fw_policy: res = self._show('firewall_policies', fw_policy['firewall_policy']['id']) self.assertEqual( 'firewall_Policy1', res['firewall_policy']['name']) def test_show_firewall_group_by_name(self): with self.firewall_group(name='fireWall1', as_admin=True) as fw: res = self._show('firewall_groups', fw['firewall_group']['id']) self.assertEqual('fireWall1', res['firewall_group']['name']) def test_set_port_in_use_for_firewall_group(self): fwg_db = {'id': 'fake_id'} new_ports = {'ports': ['fake_port1', 'fake_port2']} m_context = self._get_admin_context() with mock.patch.object(m_context.session, 'add', side_effect=[None, f_exc.FirewallGroupPortInUse( port_ids=['fake_port2'])]): self.assertRaises(f_exc.FirewallGroupPortInUse, self.db._set_ports_for_firewall_group, m_context, fwg_db, new_ports) def test_set_port_for_default_firewall_group(self): ctx = self._get_nonadmin_context() default_fwg = self._build_default_fwg(ctx=ctx) port_args = { 'tenant_id': ctx.tenant_id, 'device_owner': 'compute:nova', 'binding:vif_type': 'ovs', } self.plugin._is_supported_l2_port = mock.Mock( return_value=True) with self.port(**port_args) as port1, self.port(**port_args) as port2: port1_id = port1['port']['id'] port2_id = port2['port']['id'] port_ids = [port1_id, port2_id] self.plugin.update_firewall_group( ctx, default_fwg['id'], {'firewall_group': {'ports': port_ids}}, ) default_fwg = self.plugin.get_firewall_group(ctx, default_fwg['id']) self.assertEqual(sorted(port_ids), sorted(default_fwg['ports'])) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/0000775000175000017500000000000000000000000023533 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/__init__.py0000664000175000017500000000000000000000000025632 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/netfilter_log/0000775000175000017500000000000000000000000026370 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/netfilter_log/__init__.py0000664000175000017500000000000000000000000030467 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/netfilter_log/test_libnetfilter_log.py0000664000175000017500000001260500000000000033331 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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 socket from unittest import mock import cffi from neutron.tests import base from oslo_utils import importutils import testtools # mock for dlopen cffi.FFI = mock.Mock() cffi.FFI.dlopen = mock.Mock(return_value=mock.Mock()) lib_log = importutils.import_module( 'neutron_fwaas.privileged.netfilter_log.libnetfilter_log' ) class NFLogAppTestCase(base.BaseTestCase): def setUp(self): self.nflog_app = lib_log.NFLogApp() self.spawn = mock.patch('eventlet.spawn').start() super(NFLogAppTestCase, self).setUp() def test_register_packet_handler(self): def fake_method(): pass self.nflog_app.register_packet_handler(fake_method) self.assertEqual(fake_method, self.nflog_app.callback) def test_unregister_packet_handler(self): def fake_method(): pass self.nflog_app.register_packet_handler(fake_method) self.assertEqual(fake_method, self.nflog_app.callback) self.nflog_app.unregister_packet_handler() self.assertIsNone(self.nflog_app.callback) class NFLogWrapper(base.BaseTestCase): def setUp(self): super(NFLogWrapper, self).setUp() lib_log.libnflog = mock.Mock() lib_log.ffi = mock.Mock() def test_open_failed(self): lib_log.libnflog.nflog_open.return_value = None handle = lib_log.NFLogWrapper.get_instance() with testtools.ExpectedException(Exception): handle.open() lib_log.libnflog.nflog_open.assert_called_once_with() lib_log.libnflog.nflog_unbind_pf.assert_not_called() lib_log.libnflog.nflog_bind_pf.assert_not_called() handle.close() def test_bind_pf(self): nflog_handle = mock.Mock() lib_log.libnflog.nflog_open.return_value = nflog_handle handle = lib_log.NFLogWrapper.get_instance() handle.open() lib_log.libnflog.nflog_open.assert_called_once_with() calls = [mock.call(nflog_handle, socket.AF_INET), mock.call(nflog_handle, socket.AF_INET6)] lib_log.libnflog.nflog_unbind_pf.assert_has_calls( calls, any_order=True) lib_log.libnflog.nflog_bind_pf.assert_has_calls( calls, any_order=True) def test_bind_group_set_mode_failed(self): nflog_handle = mock.Mock() g_handle = mock.Mock() lib_log.libnflog.nflog_open.return_value = nflog_handle lib_log.libnflog.nflog_bind_group.return_value = g_handle lib_log.libnflog.nflog_set_mode.return_value = -1 handle = lib_log.NFLogWrapper.get_instance() with testtools.ExpectedException(Exception): handle.open() handle.bind_group(0) lib_log.libnflog.nflog_open.assert_called_once_with() lib_log.libnflog.nflog_bind_group.assert_called_once_with( nflog_handle, 0) lib_log.libnflog.nflog_set_mode.assert_called_once_with( g_handle, 0x2, 0xffff) lib_log.libnflog.nflog_callback_register.assert_not_called() def test_bind_group_set_callback_failed(self): nflog_handle = mock.Mock() g_handle = mock.Mock() lib_log.libnflog.nflog_open.return_value = nflog_handle lib_log.libnflog.nflog_bind_group.return_value = g_handle lib_log.libnflog.nflog_set_mode.return_value = 0 lib_log.libnflog.nflog_callback_register.return_value = -1 handle = lib_log.NFLogWrapper.get_instance() with testtools.ExpectedException(Exception): handle.open() handle.bind_group(0) lib_log.libnflog.nflog_open.assert_called_once_with() lib_log.libnflog.nflog_bind_group.assert_called_once_with( nflog_handle, 0) lib_log.libnflog.nflog_set_mode.assert_called_once_with( g_handle, 0x2, 0xffff) lib_log.libnflog.nflog_callback_register.assert_called_once_with( g_handle, handle.cb, lib_log.ffi.NULL) def test_bind_group_pass(self): nflog_handle = mock.Mock() g_handle = mock.Mock() lib_log.libnflog.nflog_open.return_value = nflog_handle lib_log.libnflog.nflog_bind_group.return_value = g_handle lib_log.libnflog.nflog_set_mode.return_value = 0 lib_log.libnflog.nflog_callback_register.return_value = 0 handle = lib_log.NFLogWrapper.get_instance() handle.open() handle.bind_group(0) lib_log.libnflog.nflog_open.assert_called_once_with() lib_log.libnflog.nflog_bind_group.assert_called_once_with( nflog_handle, 0) lib_log.libnflog.nflog_set_mode.assert_called_once_with( g_handle, 0x2, 0xffff) lib_log.libnflog.nflog_callback_register.assert_called_once_with( g_handle, handle.cb, lib_log.ffi.NULL) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/test_netlink_lib.py0000664000175000017500000003653400000000000027451 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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 testtools from neutron_lib import constants from neutron_fwaas.privileged import netlink_constants as nl_constants from neutron_fwaas.privileged import netlink_lib as nl_lib from neutron_fwaas.tests import base FAKE_ENTRY = {'ipversion': 4, 'protocol': 'icmp', 'type': '8', 'code': '0', 'id': 1234, 'src': '1.1.1.1', 'dst': '2.2.2.2'} FAKE_TCP_ENTRY = {'ipversion': 4, 'protocol': 'tcp', 'sport': 1, 'dport': 2, 'src': '1.1.1.1', 'dst': '2.2.2.2'} FAKE_UDP_ENTRY = {'ipversion': 4, 'protocol': 'udp', 'sport': 1, 'dport': 2, 'src': '1.1.1.1', 'dst': '2.2.2.2'} FAKE_ICMPV6_ENTRY = {'ipversion': 6, 'protocol': 'ipv6-icmp', 'sport': 1, 'dport': 2, 'type': '8', 'code': '0', 'id': 3456, 'src': '10::10', 'dst': '20::20'} class NetlinkLibTestCase(base.BaseTestCase): def setUp(self): super(NetlinkLibTestCase, self).setUp() nl_lib.nfct = mock.Mock() nl_lib.libc = mock.Mock() def test_open_new_conntrack_handler_failed(self): nl_lib.nfct.nfct_open.return_value = None with testtools.ExpectedException(nl_lib.ConntrackOpenFailedExit): with nl_lib.ConntrackManager(): nl_lib.nfct.nfct_open.assert_called_once() nl_lib.nfct.nfct_close.assert_not_called() def test_open_new_conntrack_handler_pass(self): with nl_lib.ConntrackManager(): nl_lib.nfct.nfct_open.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_list_entries(self): with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.list_entries() nl_lib.nfct.nfct_callback_register.assert_called_once() nl_lib.nfct.nfct_query.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_flush_entries(self): with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.flush_entries() nl_lib.nfct.nfct_query.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_new_failed(self): nl_lib.nfct.nfct_new.return_value = None with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ENTRY]) nl_lib.nfct.nfct_new.assert_called_once() nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_icmp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ENTRY['id'])), ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, conntrack._convert_text_to_binary( FAKE_ENTRY['src'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, conntrack._convert_text_to_binary( FAKE_ENTRY['dst'], 4)), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_icmpv6_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ICMPV6_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[6]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['ipv6-icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ICMPV6_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ICMPV6_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ICMPV6_ENTRY['id'])), ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV6_SRC, conntrack._convert_text_to_binary( FAKE_ENTRY['src'], 6)), mock.call(conntrack_filter, nl_constants.ATTR_IPV6_DST, conntrack._convert_text_to_binary( FAKE_ENTRY['dst'], 6)), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_udp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_UDP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['udp']) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_UDP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_UDP_ENTRY['dport'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['src'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4)), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_tcp_entry(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_TCP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['tcp']) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_TCP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_TCP_ENTRY['dport'])) ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['src'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['dst'], 4)), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() def test_conntrack_delete_entries(self): conntrack_filter = mock.Mock() nl_lib.nfct.nfct_new.return_value = conntrack_filter with nl_lib.ConntrackManager() as conntrack: nl_lib.nfct.nfct_open.assert_called_once() conntrack.delete_entries([FAKE_ENTRY, FAKE_TCP_ENTRY, FAKE_UDP_ENTRY]) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['tcp']), mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['udp']), mock.call(conntrack_filter, nl_constants.ATTR_L3PROTO, nl_constants.IPVERSION_SOCKET[4]), mock.call(conntrack_filter, nl_constants.ATTR_L4PROTO, constants.IP_PROTOCOL_MAP['icmp']), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_CODE, int(FAKE_ENTRY['code'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_TYPE, int(FAKE_ENTRY['type'])) ] nl_lib.nfct.nfct_set_attr_u8.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_TCP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_TCP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_SRC, nl_lib.libc.htons(FAKE_UDP_ENTRY['sport'])), mock.call(conntrack_filter, nl_constants.ATTR_PORT_DST, nl_lib.libc.htons(FAKE_UDP_ENTRY['dport'])), mock.call(conntrack_filter, nl_constants.ATTR_ICMP_ID, nl_lib.libc.htons(FAKE_ENTRY['id'])), ] nl_lib.nfct.nfct_set_attr_u16.assert_has_calls(calls, any_order=True) calls = [ mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['src'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, conntrack._convert_text_to_binary( FAKE_TCP_ENTRY['dst'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['src'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_SRC, conntrack._convert_text_to_binary( FAKE_ENTRY['src'], 4)), mock.call(conntrack_filter, nl_constants.ATTR_IPV4_DST, conntrack._convert_text_to_binary( FAKE_UDP_ENTRY['dst'], 4)), ] nl_lib.nfct.nfct_set_attr.assert_has_calls(calls, any_order=True) nl_lib.nfct.nfct_destroy.assert_called_once() nl_lib.nfct.nfct_close.assert_called_once() ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/privileged/test_utils.py0000664000175000017500000000557700000000000026322 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Thales Services SAS # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock import testtools from neutron_fwaas.privileged import utils from neutron_fwaas.tests import base class InNamespaceTest(base.BaseTestCase): ORG_NETNS_FD = 124 NEW_NETNS_FD = 421 NEW_NETNS = 'newns' def setUp(self): super(InNamespaceTest, self).setUp() # NOTE(cby): we should unmock os.open/close as early as possible # because there are used in cleanups open_patch = mock.patch('os.open', return_value=self.ORG_NETNS_FD) self.open_mock = open_patch.start() self.addCleanup(open_patch.stop) close_patch = mock.patch('os.close') self.close_mock = close_patch.start() self.addCleanup(close_patch.stop) self.setns_mock = mock.patch( 'pyroute2.netns.setns').start() def test_in_namespace(self): with utils.in_namespace(self.NEW_NETNS): self.setns_mock.assert_called_once_with(self.NEW_NETNS) setns_calls = [mock.call(self.NEW_NETNS), mock.call(self.ORG_NETNS_FD)] self.setns_mock.assert_has_calls(setns_calls) def test_in_no_namespace(self): for namespace in ('', None): with utils.in_namespace(namespace): pass self.setns_mock.assert_not_called() self.close_mock.assert_not_called() def test_in_namespace_failed(self): with testtools.ExpectedException(ValueError): with utils.in_namespace(self.NEW_NETNS): self.setns_mock.assert_called_once_with(self.NEW_NETNS) raise ValueError setns_calls = [mock.call(self.NEW_NETNS), mock.call(self.ORG_NETNS_FD)] self.setns_mock.assert_has_calls(setns_calls) def test_in_namespace_enter_failed(self): self.setns_mock.side_effect = ValueError with testtools.ExpectedException(ValueError): with utils.in_namespace(self.NEW_NETNS): self.fail('It should fail before we reach this code') self.setns_mock.assert_called_once_with(self.NEW_NETNS) def test_in_namespace_exit_failed(self): self.setns_mock.side_effect = [self.NEW_NETNS_FD, ValueError] with testtools.ExpectedException(utils.BackInNamespaceExit): with utils.in_namespace(self.NEW_NETNS): pass ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/0000775000175000017500000000000000000000000023224 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/__init__.py0000664000175000017500000000000000000000000025323 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/0000775000175000017500000000000000000000000025031 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/__init__.py0000664000175000017500000000000000000000000027130 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3213513 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/0000775000175000017500000000000000000000000030227 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/__init__.py0000664000175000017500000000000000000000000032326 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/0000775000175000017500000000000000000000000031510 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/__init__.py0000664000175000017500000000000000000000000033607 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/0000775000175000017500000000000000000000000033166 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/__ini0000664000175000017500000000000000000000000034154 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021300000000000011451 xustar0000000000000000111 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/ 28 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000775000175000017500000000000000000000000034246 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022000000000000011447 xustar0000000000000000122 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000000000000000000000034236 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000114 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/ 28 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000775000175000017500000000000000000000000034246 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022300000000000011452 xustar0000000000000000125 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000000000000000000000034236 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000022300000000000011452 xustar0000000000000000119 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/ 28 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000775000175000017500000000000000000000000034246 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023000000000000011450 xustar0000000000000000130 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000000000000000000000034236 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000024000000000000011451 xustar0000000000000000138 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/test_noop_driver.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000000333600000000000034255 0ustar00zuulzuul00000000000000# Copyright 2017 Mirantis Inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from unittest import mock from neutron import manager from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ noop import noop_driver from neutron_fwaas.tests import base class TestNoopDriver(base.BaseTestCase): def setUp(self): super(TestNoopDriver, self).setUp() mock_br = mock.Mock() self.firewall = noop_driver.NoopFirewallL2Driver(mock_br) def test_basic_methods(self): # just make sure it doesn't crash fwg_mock = mock.Mock() self.firewall.create_firewall_group(ports=[], firewall_group=fwg_mock) self.firewall.update_firewall_group(ports=[], firewall_group=fwg_mock) self.firewall.delete_firewall_group(ports=[], firewall_group=fwg_mock) self.firewall.filter_defer_apply_on() self.firewall.filter_defer_apply_off() self.firewall.defer_apply() self.firewall.ports def test_load_firewall_class(self): res = manager.NeutronManager.load_class_for_provider( 'neutron.agent.l2.firewall_drivers', 'noop') self.assertEqual(res, noop_driver.NoopFirewallL2Driver) ././@PaxHeader0000000000000000000000000000024300000000000011454 xustar0000000000000000135 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/ 28 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000775000175000017500000000000000000000000034246 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000025000000000000011452 xustar0000000000000000146 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000000000000000000000034236 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000025500000000000011457 xustar0000000000000000151 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_firewall.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000010007000000000000034246 0ustar00zuulzuul00000000000000# Copyright 2017 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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_lib import constants from neutron_lib.plugins.ml2 import ovs_constants as ovs_consts import testtools from neutron.agent.common import ovs_lib from neutron.plugins.ml2.drivers.openvswitch.agent import \ ovs_agent_extension_api as ovs_ext_api from neutron.tests import base from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import constants as fwaas_ovs_consts from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import exceptions from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import firewall as ovsfw TESTING_VLAN_TAG = 1 def create_ofport(port_dict): ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00', ofport=1, port_name="port-name") return ovsfw.OFPort(port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) class TestCreateRegNumbers(base.BaseTestCase): def test_no_registers_defined(self): flow = {'foo': 'bar'} ovsfw.create_reg_numbers(flow) self.assertEqual({'foo': 'bar'}, flow) def test_both_registers_defined(self): flow = {'foo': 'bar', 'reg_port': 1, 'reg_net': 2} expected_flow = {'foo': 'bar', 'reg{:d}'.format(fwaas_ovs_consts.REG_PORT): 1, 'reg{:d}'.format(fwaas_ovs_consts.REG_NET): 2} ovsfw.create_reg_numbers(flow) self.assertEqual(expected_flow, flow) class TestFirewallGroup(base.BaseTestCase): def setUp(self): super(TestFirewallGroup, self).setUp() self.fwg = ovsfw.FirewallGroup('123') self.fwg.members = {'type': [1, 2, 3, 4]} def test_update_rules(self): ingress_rules = [{'foo-ingress': 'bar', 'rule': 'all'}, {'bar-ingress': 'foo'}] egress_rules = [{'foo-egress': '123456'}, {'bar-egress': 'bar'}] self.fwg.update_rules(ingress_rules, egress_rules) self.assertEqual(ingress_rules, self.fwg.ingress_rules) self.assertEqual(egress_rules, self.fwg.egress_rules) def test_update_rules_protocols(self): # XXX FIXME(ivasilevskaya) figure out what this test does and fix # appropriately # leaving failing as it may be important rules = [ {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP, 'ethertype': constants.IPv4}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_ICMP, 'ethertype': constants.IPv6}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_IPV6_ICMP_LEGACY, 'ethertype': constants.IPv6}, {'foo': 'bar', 'protocol': constants.PROTO_NAME_TCP}, {'foo': 'bar', 'protocol': '94'}, {'foo': 'bar', 'protocol': 'baz'}, {'foo': 'no_proto'}] self.fwg.update_rules(rules, []) self.assertEqual({'foo': 'no_proto'}, self.fwg.ingress_rules.pop()) protos = [rule['protocol'] for rule in self.fwg.ingress_rules] self.assertEqual([constants.PROTO_NUM_ICMP, constants.PROTO_NUM_IPV6_ICMP, constants.PROTO_NUM_IPV6_ICMP, constants.PROTO_NUM_TCP, 94, 'baz'], protos) def test_get_ethertype_filtered_addresses(self): addresses = self.fwg.get_ethertype_filtered_addresses('type') expected_addresses = [1, 2, 3, 4] self.assertEqual(expected_addresses, addresses) class TestOFPort(base.BaseTestCase): def setUp(self): super(TestOFPort, self).setUp() self.ipv4_addresses = ['10.0.0.1', '192.168.0.1'] self.ipv6_addresses = ['fe80::f816:3eff:fe2e:1'] port_dict = {'device': 1, 'fixed_ips': [ {'subnet_id': 's_%s' % ip, 'ip_address': ip} for ip in self.ipv4_addresses + self.ipv6_addresses]} self.port = create_ofport(port_dict) def test_ipv4_address(self): ipv4_addresses = self.port.ipv4_addresses self.assertEqual(self.ipv4_addresses, ipv4_addresses) def test_ipv6_address(self): ipv6_addresses = self.port.ipv6_addresses self.assertEqual(self.ipv6_addresses, ipv6_addresses) def test__get_allowed_pairs(self): port = { 'allowed_address_pairs': [ {'mac_address': 'foo', 'ip_address': '10.0.0.1'}, {'mac_address': 'bar', 'ip_address': '192.168.0.1'}, {'mac_address': 'qux', 'ip_address': '169.254.0.0/16'}, {'mac_address': 'baz', 'ip_address': '2003::f'}, ]} allowed_pairs_v4 = ovsfw.OFPort._get_allowed_pairs(port, version=4) allowed_pairs_v6 = ovsfw.OFPort._get_allowed_pairs(port, version=6) expected_aap_v4 = {('foo', '10.0.0.1'), ('bar', '192.168.0.1'), ('qux', '169.254.0.0/16')} expected_aap_v6 = {('baz', '2003::f')} self.assertEqual(expected_aap_v4, allowed_pairs_v4) self.assertEqual(expected_aap_v6, allowed_pairs_v6) def test__get_allowed_pairs_empty(self): port = {} allowed_pairs = ovsfw.OFPort._get_allowed_pairs(port, version=4) self.assertFalse(allowed_pairs) def test_update(self): old_port_dict = self.port.neutron_port_dict new_port_dict = old_port_dict.copy() added_ips = [1, 2, 3] new_port_dict.update({ 'fixed_ips': added_ips, 'allowed_address_pairs': [ {'mac_address': '00:00:00:00:00:01', 'ip_address': '192.168.0.1'}, {'mac_address': '00:00:00:00:00:01', 'ip_address': '2003::f'}], }) self.port.update(new_port_dict) self.assertEqual(new_port_dict, self.port.neutron_port_dict) self.assertIsNot(new_port_dict, self.port.neutron_port_dict) self.assertEqual(added_ips, self.port.fixed_ips) self.assertEqual({('00:00:00:00:00:01', '192.168.0.1')}, self.port.allowed_pairs_v4) self.assertIn(('00:00:00:00:00:01', '2003::f'), self.port.allowed_pairs_v6) class TestFWGPortMap(base.BaseTestCase): def setUp(self): super(TestFWGPortMap, self).setUp() self.map = ovsfw.FWGPortMap() def test_get_or_create_fwg_existing_fwg(self): self.map.fw_groups['id'] = mock.sentinel fwg = self.map.get_or_create_fwg('id') self.assertIs(mock.sentinel, fwg) def test_get_or_create_fwg_nonexisting_fwg(self): with mock.patch.object(ovsfw, 'FirewallGroup') as fwg_mock: fwg = self.map.get_or_create_fwg('id') self.assertEqual(fwg_mock.return_value, fwg) def _check_port(self, port_id, expected_id): port = self.map.ports[port_id] expected_fwg = self.map.fw_groups[expected_id] self.assertEqual(expected_fwg, port.fw_group) def _check_fwg(self, fwg_id, expected_port_ids): fwg = self.map.fw_groups[fwg_id] expected_ports = {self.map.ports[port_id] for port_id in expected_port_ids} self.assertEqual(expected_ports, fwg.ports) def _create_ports_and_fwgs(self): fwg_1 = ovsfw.FirewallGroup(1) fwg_2 = ovsfw.FirewallGroup(2) fwg_3 = ovsfw.FirewallGroup(3) port_a = create_ofport({'device': 'a'}) port_b = create_ofport({'device': 'b'}) port_c = create_ofport({'device': 'c'}) self.map.ports = {'a': port_a, 'b': port_b, 'c': port_c} self.map.fw_groups = {1: fwg_1, 2: fwg_2, 3: fwg_3} # XXX FIXME(ivasilevskaya) see note for OFPORT port_a.fw_group = fwg_1 port_b.fw_group = fwg_2 port_c.fw_group = fwg_2 fwg_1.ports = {port_a} fwg_2.ports = {port_b, port_c} def test_create_port(self): """Create a port and assign it to firewall group It is implied that 1 port can be assigned to one firewall group only """ port = create_ofport({'device': 'a'}) port_dict = {'some-port-attributes-go-here': 42, 'firewall_group': 1} self.map.create_port(port, port_dict) self._check_port('a', 1) self._check_fwg(1, ['a']) def test_update_port_another_fwg_added(self): """Update a port with new firewall group id It is implied that 1 port can be assigned to one firewall group only """ self._create_ports_and_fwgs() self._check_port('b', 2) port_dict = {'firewall_group': 3} self.map.update_port(self.map.ports['b'], port_dict) self._check_port('a', 1) self._check_port('b', 3) self._check_port('c', 2) self._check_fwg(1, ['a']) self._check_fwg(2, ['c']) self._check_fwg(3, ['b']) def test_remove_port(self): self._create_ports_and_fwgs() self.map.remove_port(self.map.ports['c']) self._check_port('b', 2) self._check_fwg(1, ['a']) self._check_fwg(2, ['b']) self.assertNotIn('c', self.map.ports) def test_update_rules(self): """Just make sure it doesn't crash""" self.map.update_rules(42, [], []) def test_update_members(self): """Just make sure it doesn't crash""" self.map.update_members(42, []) class FakeOVSPort(object): def __init__(self, name, port, mac): self.port_name = name self.ofport = port self.vif_mac = mac class TestOVSFirewallDriver(base.BaseTestCase): def setUp(self): super(TestOVSFirewallDriver, self).setUp() self._mock_ovs_br = mock.patch.object( ovs_lib, 'OVSBridge', autospec=True) mock_bridge = self._mock_ovs_br.start() self.addCleanup(self._mock_ovs_br.stop) mock_agent_api = mock.patch.object( ovs_ext_api.OVSAgentExtensionAPI, 'request_int_br', return_value=mock_bridge).start() self.firewall = ovsfw.OVSFirewallDriver(mock_agent_api) self.mock_bridge = self.firewall.int_br self.mock_bridge.reset_mock() self.fake_ovs_port = FakeOVSPort('port', 1, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ self.fake_ovs_port def _prepare_firewall_group(self): ingress_rules = [ {'position': '1', 'protocol': 'tcp', 'ip_version': 4, 'destination_port': '123', 'enabled': True, 'action': 'allow', 'id': 'fake-fw-rule1'} ] egress_rules = [ {'position': '2', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'allow', 'id': 'fake-fw-rule2'}, {'position': '3', 'protocol': 'tcp', 'ip_version': 6, 'enabled': True, 'action': 'allow', 'id': 'fake-fw-rule3'}] self.firewall.update_firewall_group_rules(1, ingress_rules, []) self.firewall.update_firewall_group_rules(2, [], egress_rules) @property def port_ofport(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.ofport @property def port_mac(self): return self.mock_bridge.br.get_vif_port_by_id.return_value.vif_mac def test_initialize_bridge(self): br = self.firewall.initialize_bridge(self.mock_bridge) self.assertEqual(br, self.mock_bridge.deferred.return_value) def test__add_flow_dl_type_formatted_to_string(self): dl_type = 0x0800 self.firewall._add_flow(dl_type=dl_type) def test__add_flow_registers_are_replaced(self): self.firewall._add_flow(in_port=1, reg_port=1, reg_net=2) expected_calls = {'in_port': 1, 'reg{:d}'.format(fwaas_ovs_consts.REG_PORT): 1, 'reg{:d}'.format(fwaas_ovs_consts.REG_NET): 2} self.mock_bridge.br.add_flow.assert_called_once_with( **expected_calls) def test__drop_all_unmatched_flows(self): self.firewall._drop_all_unmatched_flows() expected_calls = [ mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_BASE_EGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), mock.call(actions='drop', priority=0, table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE)] actual_calls = self.firewall.int_br.br.add_flow.call_args_list self.assertEqual(expected_calls, actual_calls) def test_get_or_create_ofport_non_existing(self): port_dict = { 'device': 'port-id', 'firewall_group': 123, 'lvlan': TESTING_VLAN_TAG, } port = self.firewall.get_or_create_ofport(port_dict) port_dict = { 'device': 'port-id', 'firewall_group': 456, 'lvlan': TESTING_VLAN_TAG, } port = self.firewall.get_or_create_ofport(port_dict) sg1, sg2 = sorted( self.firewall.fwg_port_map.fw_groups.values(), key=lambda x: x.id) self.assertIn(port, self.firewall.fwg_port_map.ports.values()) self.assertEqual(port.fw_group, sg2) self.assertEqual(set(), sg1.ports) self.assertIn(port, sg2.ports) def test_get_or_create_ofport_existing(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} of_port = create_ofport(port_dict) self.firewall.fwg_port_map.ports[of_port.id] = of_port port = self.firewall.get_or_create_ofport(port_dict) [sg1] = sorted(self.firewall.fwg_port_map.fw_groups.values(), key=lambda x: x.id) self.assertIs(of_port, port) self.assertIn(port, self.firewall.fwg_port_map.ports.values()) self.assertEqual(port.fw_group, sg1) self.assertIn(port, sg1.ports) def test_get_or_create_ofport_changed(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} of_port = create_ofport(port_dict) self.firewall.fwg_port_map.ports[of_port.id] = of_port fake_ovs_port = FakeOVSPort('port', 2, '00:00:00:00:00:00') self.mock_bridge.br.get_vif_port_by_id.return_value = \ fake_ovs_port port = self.firewall.get_or_create_ofport(port_dict) self.assertEqual(port.ofport, 2) def test_get_or_create_ofport_missing(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} self.mock_bridge.br.get_vif_port_by_id.return_value = None with testtools.ExpectedException(exceptions.OVSFWaaSPortNotFound): self.firewall.get_or_create_ofport(port_dict) def test_get_or_create_ofport_missing_nocreate(self): port_dict = { 'device': 'port-id', 'firewall_group': 123} self.mock_bridge.br.get_vif_port_by_id.return_value = None self.assertIsNone(self.firewall.get_ofport(port_dict)) self.assertFalse(self.mock_bridge.br.get_vif_port_by_id.called) def test_is_port_managed_managed_port(self): port_dict = {'device': 'port-id'} self.firewall.fwg_port_map.ports[port_dict['device']] = object() is_managed = self.firewall.is_port_managed(port_dict) self.assertTrue(is_managed) def test_is_port_managed_not_managed_port(self): port_dict = {'device': 'port-id'} is_managed = self.firewall.is_port_managed(port_dict) self.assertFalse(is_managed) def test_prepare_port_filter(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'fixed_ips': [{'subnet_id': "some_subnet_id_here", 'ip_address': "10.0.0.1"}], 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) exp_egress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_EGRESS_TABLE), in_port=self.port_ofport, priority=105, table=ovs_consts.TRANSIENT_TABLE) exp_ingress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'strip_vlan,resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), dl_dst=self.port_mac, dl_vlan='0x%x' % TESTING_VLAN_TAG, priority=95, table=ovs_consts.TRANSIENT_TABLE) filter_rule = mock.call( actions='ct(commit,zone=NXM_NX_REG6[0..15]),' 'output:{:d},resubmit(,{:d})'.format( self.port_ofport, ovs_consts.ACCEPTED_INGRESS_TRAFFIC_TABLE), dl_type="0x{:04x}".format(constants.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=70, reg5=self.port_ofport, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, tcp_dst='0x007b') calls = self.mock_bridge.br.add_flow.call_args_list for call in exp_ingress_classifier, exp_egress_classifier, filter_rule: self.assertIn(call, calls) def test_prepare_port_filter_in_coexistence_mode(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'fixed_ips': [{'subnet_id': "some_subnet_id_here", 'ip_address': "10.0.0.1"}], 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.sg_with_ovs = True self.firewall.prepare_port_filter(port_dict) exp_egress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_EGRESS_TABLE), in_port=self.port_ofport, priority=105, table=ovs_consts.TRANSIENT_TABLE) exp_ingress_classifier = mock.call( actions='set_field:{:d}->reg5,set_field:{:d}->reg6,' 'strip_vlan,resubmit(,{:d})'.format( self.port_ofport, TESTING_VLAN_TAG, fwaas_ovs_consts.FW_BASE_INGRESS_TABLE), dl_dst=self.port_mac, dl_vlan='0x%x' % TESTING_VLAN_TAG, priority=95, table=ovs_consts.TRANSIENT_TABLE) filter_rule = mock.call( actions='resubmit(,{:d})'.format(ovs_consts.RULES_INGRESS_TABLE), dl_type="0x{:04x}".format(constants.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_TCP, priority=70, reg5=self.port_ofport, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, table=fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, tcp_dst='0x007b') calls = self.mock_bridge.br.add_flow.call_args_list for call in exp_ingress_classifier, exp_egress_classifier, filter_rule: self.assertIn(call, calls) def test_prepare_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'port_security_enabled': False} self._prepare_firewall_group() with mock.patch.object( self.firewall, 'initialize_port_flows') as m_init_flows: self.firewall.prepare_port_filter(port_dict) self.assertFalse(m_init_flows.called) def test_prepare_port_filter_initialized_port(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) self.assertFalse(self.mock_bridge.br.delete_flows.called) self.firewall.prepare_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) def test_update_port_filter(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) port_dict['firewall_group'] = 2 self.mock_bridge.reset_mock() self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) filter_rules = [ mock.call( actions='resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), dl_type="0x{:04x}".format(constants.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_UDP, priority=71, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE), # XXX FIXME NOTE(ivasilevskaya) this test originally tested that # flows for SG with remote_group=this group were generated with # proper conjunction action. If the original idea that conj_manager # isn't needed for firewall groups proves to be wrong this needs to # be revizited and properly fixed/covered with tests mock.call( actions='resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY, dl_type=mock.ANY, nw_proto=6, priority=70, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE)] self.mock_bridge.br.add_flow.assert_has_calls(filter_rules, any_order=True) def test_update_port_filter_in_coexistence_mode(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.sg_with_ovs = True self.firewall.prepare_port_filter(port_dict) port_dict['firewall_group'] = 2 self.mock_bridge.reset_mock() self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) filter_rules = [ mock.call( actions='resubmit(,{:d})'.format( ovs_consts.RULES_EGRESS_TABLE), dl_type="0x{:04x}".format(constants.ETHERTYPE_IP), nw_proto=constants.PROTO_NUM_UDP, priority=71, ct_state=fwaas_ovs_consts.OF_STATE_NEW_NOT_ESTABLISHED, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE), # XXX FIXME NOTE(ivasilevskaya) this test originally tested that # flows for SG with remote_group=this group were generated with # proper conjunction action. If the original idea that conj_manager # isn't needed for firewall groups proves to be wrong this needs to # be revizited and properly fixed/covered with tests mock.call( actions='resubmit(,{:d})'.format( ovs_consts.RULES_EGRESS_TABLE), ct_state=fwaas_ovs_consts.OF_STATE_ESTABLISHED_NOT_REPLY, dl_type=mock.ANY, nw_proto=6, priority=70, reg5=self.port_ofport, table=fwaas_ovs_consts.FW_RULES_EGRESS_TABLE)] self.mock_bridge.br.add_flow.assert_has_calls(filter_rules, any_order=True) def test_update_port_filter_create_new_port_if_not_present(self): port_dict = {'device': 'port-id', 'firewall_group': 1} self._prepare_firewall_group() with mock.patch.object( self.firewall, 'prepare_port_filter') as prepare_mock: self.firewall.update_port_filter(port_dict) self.assertTrue(prepare_mock.called) def test_update_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) port_dict['port_security_enabled'] = False self.firewall.update_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) def test_remove_port_filter(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self._prepare_firewall_group() self.firewall.prepare_port_filter(port_dict) self.firewall.remove_port_filter(port_dict) self.assertTrue(self.mock_bridge.br.delete_flows.called) self.assertIn(1, self.firewall.fwg_to_delete) def test_remove_port_filter_port_security_disabled(self): port_dict = {'device': 'port-id', 'firewall_group': 1} self.firewall.remove_port_filter(port_dict) self.assertFalse(self.mock_bridge.br.delete_flows.called) def test_update_firewall_group_rules(self): """Just make sure it doesn't crash""" new_rules_ingress = [ {'ip_version': 4, 'action': 'allow', 'protocol': constants.PROTO_NAME_ICMP}, {'ip_version': 4, 'direction': 'deny'}] self.firewall.update_firewall_group_rules(1, new_rules_ingress, []) def test__cleanup_stale_sg(self): self._prepare_firewall_group() self.firewall.fwg_to_delete = {1} with mock.patch.object(self.firewall.fwg_port_map, 'delete_fwg') as delete_fwg_mock: self.firewall._cleanup_stale_fwg() delete_fwg_mock.assert_called_once_with(1) def test_get_ovs_port(self): ovs_port = self.firewall.get_ovs_port('port_id') self.assertEqual(self.fake_ovs_port, ovs_port) def test_get_ovs_port_non_existent(self): self.mock_bridge.br.get_vif_port_by_id.return_value = None with testtools.ExpectedException(exceptions.OVSFWaaSPortNotFound): self.firewall.get_ovs_port('port_id') def test__initialize_egress_no_port_security_sends_to_egress(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': TESTING_VLAN_TAG} self.firewall._initialize_egress_no_port_security(port_dict) expected_call = mock.call( table=ovs_consts.TRANSIENT_TABLE, priority=100, in_port=self.fake_ovs_port.ofport, actions='set_field:%d->reg%d,' 'set_field:%d->reg%d,' 'resubmit(,%d)' % ( self.fake_ovs_port.ofport, fwaas_ovs_consts.REG_PORT, TESTING_VLAN_TAG, fwaas_ovs_consts.REG_NET, fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE) ) calls = self.mock_bridge.br.add_flow.call_args_list self.assertIn(expected_call, calls) def test__initialize_egress_no_port_security_no_tag(self): port_dict = {'device': 'port-id', 'firewall_group': 1, 'lvlan': None} self.firewall._initialize_egress_no_port_security(port_dict) self.assertFalse(self.mock_bridge.br.add_flow.called) def test__remove_egress_no_port_security_deletes_flow(self): self.mock_bridge.br.db_get_val.return_value = {'tag': TESTING_VLAN_TAG} self.firewall.fwg_port_map.unfiltered['port_id'] = 1 self.firewall._remove_egress_no_port_security('port_id') expected_call = mock.call( table=ovs_consts.TRANSIENT_TABLE, in_port=self.fake_ovs_port.ofport, ) calls = self.mock_bridge.br.delete_flows.call_args_list self.assertIn(expected_call, calls) def test__remove_egress_no_port_security_no_tag(self): self.mock_bridge.br.db_get_val.return_value = {} self.firewall._remove_egress_no_port_security('port_id') self.assertFalse(self.mock_bridge.br.delete_flows.called) def test_add_flows_from_rules_with_dst_large_than_src_port(self): port_dict = { 'device': 'port-id', 'firewall_group': 123, 'lvlan': TESTING_VLAN_TAG, } port = self.firewall.get_or_create_ofport(port_dict) ingress_rules = [{ 'ip_version': 4, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6, 'direction': 'ingress', 'ethertype': 'IPv4', 'offset': 1, 'port_range_min': 7000, 'port_range_max': 7000, 'source_port_range_min': 4000, 'source_port_range_max': 4000 }, { 'ip_version': 6, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6, 'direction': 'ingress', 'ethertype': 'IPv6', 'offset': 0, 'port_range_min': 7000, 'port_range_max': 7000, 'source_port_range_min': 4000, 'source_port_range_max': 4000 }] egress_rules = [{ 'ip_version': 4, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6, 'direction': 'egress', 'ethertype': 'IPv4', 'offset': 1, 'port_range_min': 7000, 'port_range_max': 7000, 'source_port_range_min': 4000, 'source_port_range_max': 4000 }, { 'ip_version': 6, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6, 'direction': 'egress', 'ethertype': 'IPv6', 'offset': 0, 'port_range_min': 7000, 'port_range_max': 7000, 'source_port_range_min': 4000, 'source_port_range_max': 4000 }] port.fw_group.ingress_rules = [{ 'ip_version': 4, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6 }, { 'ip_version': 6, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6 } ] port.fw_group.egress_rules = [{ 'ip_version': 4, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6 }, { 'ip_version': 6, 'source_port': '4000', 'destination_port': '7000', 'protocol': 6 } ] self.firewall.add_flows_from_rules(port) self.assertEqual(ingress_rules, port.fw_group.ingress_rules) self.assertEqual(egress_rules, port.fw_group.egress_rules) ././@PaxHeader0000000000000000000000000000025200000000000011454 xustar0000000000000000148 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_rules.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000003142300000000000034253 0ustar00zuulzuul00000000000000# Copyright 2015 Red Hat, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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_lib import constants from neutron.tests import base from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import constants as fwaas_ovs_consts from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import firewall as ovsfw from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.\ openvswitch_firewall import rules TESTING_VLAN_TAG = 1 class TestIsValidPrefix(base.BaseTestCase): def test_valid_prefix_ipv4(self): is_valid = rules.is_valid_prefix('10.0.0.0/0') self.assertTrue(is_valid) def test_invalid_prefix_ipv4(self): is_valid = rules.is_valid_prefix('0.0.0.0/0') self.assertFalse(is_valid) def test_valid_prefix_ipv6(self): is_valid = rules.is_valid_prefix('ffff::0/0') self.assertTrue(is_valid) def test_invalid_prefix_ipv6(self): is_valid = rules.is_valid_prefix('0000:0::0/0') self.assertFalse(is_valid) is_valid = rules.is_valid_prefix('::0/0') self.assertFalse(is_valid) is_valid = rules.is_valid_prefix('::/0') self.assertFalse(is_valid) class TestCreateFlowsFromRuleAndPort(base.BaseTestCase): def setUp(self): super(TestCreateFlowsFromRuleAndPort, self).setUp() ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00') ovs_port.ofport = 1 port_dict = {'device': 'port_id'} self.port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) self._mock_create_flows = mock.patch.object( rules, 'create_protocol_flows') self.create_flows_mock = self._mock_create_flows.start() self.addCleanup(self._mock_create_flows.stop) @property def passed_flow_template(self): return self.create_flows_mock.call_args[0][1] def _test_create_flows_from_rule_and_port_helper( self, rule, expected_template): rules.create_flows_from_rule_and_port(rule, self.port) self.assertEqual(expected_template, self.passed_flow_template) def test_create_flows_from_rule_and_port_no_ip_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, } expected_template = { 'priority': 70, 'dl_type': constants.ETHERTYPE_IP, 'reg_port': self.port.ofport, } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '192.168.0.0/24', 'dest_ip_prefix': '10.0.0.1/32', } expected_template = { 'priority': 70, 'dl_type': constants.ETHERTYPE_IP, 'reg_port': self.port.ofport, 'nw_src': '192.168.0.0/24', 'nw_dst': '10.0.0.1/32', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_with_zero_ipv4(self): rule = { 'ethertype': constants.IPv4, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '192.168.0.0/24', 'dest_ip_prefix': '0.0.0.0/0', } expected_template = { 'priority': 70, 'dl_type': constants.ETHERTYPE_IP, 'reg_port': self.port.ofport, 'nw_src': '192.168.0.0/24', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_no_ip_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': constants.INGRESS_DIRECTION, } expected_template = { 'priority': 70, 'dl_type': constants.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '2001:db8:bbbb::1/64', 'dest_ip_prefix': '2001:db8:aaaa::1/64', } expected_template = { 'priority': 70, 'dl_type': constants.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, 'ipv6_src': '2001:db8:bbbb::1/64', 'ipv6_dst': '2001:db8:aaaa::1/64', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) def test_create_flows_from_rule_and_port_src_and_dst_with_zero_ipv6(self): rule = { 'ethertype': constants.IPv6, 'direction': constants.INGRESS_DIRECTION, 'source_ip_prefix': '2001:db8:bbbb::1/64', 'dest_ip_prefix': '::/0', } expected_template = { 'priority': 70, 'dl_type': constants.ETHERTYPE_IPV6, 'reg_port': self.port.ofport, 'ipv6_src': '2001:db8:bbbb::1/64', } self._test_create_flows_from_rule_and_port_helper(rule, expected_template) class TestCreateProtocolFlows(base.BaseTestCase): def setUp(self): super(TestCreateProtocolFlows, self).setUp() ovs_port = mock.Mock(vif_mac='00:00:00:00:00:00') ovs_port.ofport = 1 port_dict = {'device': 'port_id'} self.port = ovsfw.OFPort( port_dict, ovs_port, vlan_tag=TESTING_VLAN_TAG) def _test_create_protocol_flows_helper(self, direction, rule, expected_flows): flow_template = {'some_settings': 'foo'} for flow in expected_flows: flow.update(flow_template) flows = rules.create_protocol_flows( direction, flow_template, self.port, rule) self.assertEqual(expected_flows, flows) def test_create_protocol_flows_ingress(self): rule = {'protocol': constants.PROTO_NUM_TCP} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_INGRESS_TABLE, 'actions': 'output:1', 'nw_proto': constants.PROTO_NUM_TCP }] self._test_create_protocol_flows_helper( constants.INGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_egress(self): rule = {'protocol': constants.PROTO_NUM_TCP} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_TCP, }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_no_protocol(self): rule = {} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_icmp6(self): rule = {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NUM_IPV6_ICMP} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_IPV6_ICMP, }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_port_range(self): rule = {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NUM_TCP, 'port_range_min': 22, 'port_range_max': 23} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_TCP, 'tcp_dst': '0x0016/0xfffe' }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_icmp(self): rule = {'ethertype': constants.IPv4, 'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 0} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_ICMP, 'icmp_type': 0 }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) def test_create_protocol_flows_ipv6_icmp(self): rule = {'ethertype': constants.IPv6, 'protocol': constants.PROTO_NUM_IPV6_ICMP, 'port_range_min': 5, 'port_range_max': 0} expected_flows = [{ 'table': fwaas_ovs_consts.FW_RULES_EGRESS_TABLE, 'actions': 'resubmit(,{:d})'.format( fwaas_ovs_consts.FW_ACCEPT_OR_INGRESS_TABLE), 'nw_proto': constants.PROTO_NUM_IPV6_ICMP, 'icmp_type': 5, 'icmp_code': 0, }] self._test_create_protocol_flows_helper( constants.EGRESS_DIRECTION, rule, expected_flows) class TestCreatePortRangeFlows(base.BaseTestCase): def _test_create_port_range_flows_helper(self, expected_flows, rule): flow_template = {'some_settings': 'foo'} for flow in expected_flows: flow.update(flow_template) port_range_flows = rules.create_port_range_flows(flow_template, rule) self.assertEqual(expected_flows, port_range_flows) def test_create_port_range_flows_with_source_and_destination(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'source_port_range_min': 123, 'source_port_range_max': 124, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [ {'tcp_src': '0x007b', 'tcp_dst': '0x000a/0xfffe'}, {'tcp_src': '0x007c', 'tcp_dst': '0x000a/0xfffe'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_with_source(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'source_port_range_min': 123, 'source_port_range_max': 124, } expected_flows = [ {'tcp_src': '0x007b'}, {'tcp_src': '0x007c'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_with_destination(self): rule = { 'protocol': constants.PROTO_NUM_TCP, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [ {'tcp_dst': '0x000a/0xfffe'}, ] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_flows_without_port_range(self): rule = { 'protocol': constants.PROTO_NUM_TCP, } expected_flows = [] self._test_create_port_range_flows_helper(expected_flows, rule) def test_create_port_range_with_icmp_protocol(self): # NOTE: such call is prevented by create_protocols_flows rule = { 'protocol': constants.PROTO_NUM_ICMP, 'port_range_min': 10, 'port_range_max': 11, } expected_flows = [] self._test_create_port_range_flows_helper(expected_flows, rule) ././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas_v2.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000005606600000000000034265 0ustar00zuulzuul00000000000000# Copyright (c) 2016 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from unittest import mock from neutron.tests import base from neutron.tests.unit.api.v2 import test_base as test_api_v2 import neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.\ iptables_fwaas_v2 as fwaas _uuid = test_api_v2._uuid FAKE_SRC_PREFIX = '10.0.0.0/24' FAKE_DST_PREFIX = '20.0.0.0/24' FAKE_PROTOCOL = 'tcp' FAKE_SRC_PORT = 5000 FAKE_DST_PORT = 22 FAKE_FW_ID = 'fake-fw-uuid' FAKE_PORT_IDS = ('1_fake-port-uuid', '2_fake-port-uuid') FW_LEGACY = 'legacy' MAX_INTF_NAME_LEN = 14 class IptablesFwaasTestCase(base.BaseTestCase): def setUp(self): super(IptablesFwaasTestCase, self).setUp() self.iptables_cls_p = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') self.iptables_cls_p.start() self.firewall = fwaas.IptablesFwaasDriver() self.firewall.conntrack.delete_entries = mock.Mock() self.firewall.conntrack.flush_entries = mock.Mock() def _fake_rules_v4(self, fwid, apply_list): rule_list = [] rule1 = {'enabled': True, 'action': 'allow', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '80', 'source_ip_address': '10.24.4.2', 'id': 'fake-fw-rule1'} rule2 = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '22', 'id': 'fake-fw-rule2'} rule3 = {'enabled': True, 'action': 'reject', 'ip_version': 4, 'protocol': 'tcp', 'destination_port': '23', 'id': 'fake-fw-rule3'} ingress_chain = ('iv4%s' % fwid)[:11] egress_chain = ('ov4%s' % fwid)[:11] for router_info_inst, port_ids in apply_list: v4filter_inst = router_info_inst.iptables_manager.ipv4['filter'] v4filter_inst.chains.append(ingress_chain) v4filter_inst.chains.append(egress_chain) rule_list.append(rule1) rule_list.append(rule2) rule_list.append(rule3) return rule_list def _fake_rules_v6(self, fwid, apply_list): rule_list = [] rule1 = {'enabled': True, 'action': 'allow', 'ip_version': 6, 'protocol': 'icmp', 'destination_ip_address': '2001:db8::2', 'id': 'fake-fw-rule1'} ingress_chain = ('iv6%s' % fwid)[:11] egress_chain = ('ov6%s' % fwid)[:11] for router_info_inst, port_ids in apply_list: v6filter_inst = router_info_inst.iptables_manager.ipv6['filter'] v6filter_inst.chains.append(ingress_chain) v6filter_inst.chains.append(egress_chain) rule_list.append(rule1) return rule_list def _fake_firewall_no_rule(self): rule_list = [] fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': True, 'tenant_id': 'tenant-uuid', 'egress_rule_list': rule_list, 'ingress_rule_list': rule_list} return fw_inst def _fake_firewall(self, rule_list): _rule_list = copy.deepcopy(rule_list) for rule in _rule_list: rule['position'] = str(_rule_list.index(rule)) fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': True, 'tenant_id': 'tenant-uuid', 'egress_rule_list': _rule_list, 'ingress_rule_list': _rule_list} return fw_inst def _fake_firewall_with_admin_down(self, rule_list): fw_inst = {'id': FAKE_FW_ID, 'admin_state_up': False, 'tenant_id': 'tenant-uuid', 'egress_rule_list': rule_list, 'ingress_rule_list': rule_list} return fw_inst def _fake_apply_list(self, router_count=1, distributed=False, distributed_mode=None): apply_list = [] while router_count > 0: iptables_inst = mock.Mock() if distributed: router_inst = {'distributed': distributed} else: router_inst = {} v4filter_inst = mock.Mock() v6filter_inst = mock.Mock() v4filter_inst.chains = [] v6filter_inst.chains = [] iptables_inst.ipv4 = {'filter': v4filter_inst} iptables_inst.ipv6 = {'filter': v6filter_inst} router_info_inst = mock.Mock() router_info_inst.iptables_manager = iptables_inst router_info_inst.snat_iptables_manager = iptables_inst if distributed_mode == 'dvr': router_info_inst.rtr_fip_connect = True router_info_inst.router = router_inst apply_list.append((router_info_inst, FAKE_PORT_IDS)) router_count -= 1 return apply_list def _get_intf_name(self, if_prefix, port_id): _name = "%s%s" % (if_prefix, port_id) return _name[:MAX_INTF_NAME_LEN] def _setup_firewall_with_rules(self, func, router_count=1, distributed=False, distributed_mode=None): apply_list = self._fake_apply_list(router_count=router_count, distributed=distributed, distributed_mode=distributed_mode) rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) if distributed: if distributed_mode == 'dvr_snat': if_prefix = 'sg-' if distributed_mode == 'dvr': if_prefix = 'rfp-' else: if_prefix = 'qr-' distributed_mode = 'legacy' func(distributed_mode, apply_list, firewall) binary_name = fwaas.iptables_manager.binary_name dropped = '%s-dropped' % binary_name accepted = '%s-accepted' % binary_name rejected = '%s-rejected' % binary_name invalid_rule = '-m state --state INVALID -j %s' % dropped est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' rule1 = '-p tcp -s 10.24.4.2/32 -m tcp --dport 80 -j %s' % accepted rule2 = '-p tcp -m tcp --dport 22 -j %s' % dropped rule3 = '-p tcp -m tcp --dport 23 -j %s' % rejected ingress_chain = 'iv4%s' % firewall['id'] egress_chain = 'ov4%s' % firewall['id'] ipt_mgr_ichain = '%s-%s' % (binary_name, ingress_chain[:11]) ipt_mgr_echain = '%s-%s' % (binary_name, egress_chain[:11]) for router_info_inst, port_ids in apply_list: v4filter_inst = router_info_inst.iptables_manager.ipv4['filter'] calls = [mock.call.remove_chain('iv4fake-fw-uuid'), mock.call.remove_chain('ov4fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule( 'fwaas-default-policy', '-j %s' % dropped), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule), mock.call.add_rule(ingress_chain, rule1), mock.call.add_rule(ingress_chain, rule2), mock.call.add_rule(ingress_chain, rule3), mock.call.add_rule(egress_chain, rule1), mock.call.add_rule(egress_chain, rule2), mock.call.add_rule(egress_chain, rule3) ] for port in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port) calls.append(mock.call.add_rule('FORWARD', '-o %s -j %s' % (intf_name, ipt_mgr_ichain))) for port in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port) calls.append(mock.call.add_rule('FORWARD', '-i %s -j %s' % (intf_name, ipt_mgr_echain))) for direction in ['o', 'i']: for port_id in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port_id) calls.append(mock.call.add_rule('FORWARD', '-%s %s -j %s-fwaas-defau' % (direction, intf_name, binary_name))) v4filter_inst.assert_has_calls(calls) def _setup_firewall_with_rules_v6(self, func, router_count=1, distributed=False, distributed_mode=None): apply_list = self._fake_apply_list(router_count=router_count, distributed=distributed, distributed_mode=distributed_mode) rule_list = self._fake_rules_v6(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) if distributed: if distributed_mode == 'dvr_snat': if_prefix = 'sg-' if distributed_mode == 'dvr': if_prefix = 'rfp-' else: if_prefix = 'qr-' distributed_mode = 'legacy' func(distributed_mode, apply_list, firewall) binary_name = fwaas.iptables_manager.binary_name dropped = '%s-dropped' % binary_name accepted = '%s-accepted' % binary_name invalid_rule = '-m state --state INVALID -j %s' % dropped est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' rule1 = '-p ipv6-icmp -d 2001:db8::2/128 -j %s' % accepted ingress_chain = 'iv6%s' % firewall['id'] egress_chain = 'ov6%s' % firewall['id'] ipt_mgr_ichain = '%s-%s' % (binary_name, ingress_chain[:11]) ipt_mgr_echain = '%s-%s' % (binary_name, egress_chain[:11]) for router_info_inst, port_ids in apply_list: v6filter_inst = router_info_inst.iptables_manager.ipv6['filter'] calls = [mock.call.remove_chain('iv6fake-fw-uuid'), mock.call.remove_chain('ov6fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule( 'fwaas-default-policy', '-j %s' % dropped), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule), mock.call.add_rule(ingress_chain, rule1), mock.call.add_rule(egress_chain, rule1) ] for port in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port) calls.append(mock.call.add_rule('FORWARD', '-o %s -j %s' % (intf_name, ipt_mgr_ichain))) for port in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port) calls.append(mock.call.add_rule('FORWARD', '-i %s -j %s' % (intf_name, ipt_mgr_echain))) for direction in ['o', 'i']: for port_id in FAKE_PORT_IDS: intf_name = self._get_intf_name(if_prefix, port_id) calls.append(mock.call.add_rule('FORWARD', '-%s %s -j %s-fwaas-defau' % (direction, intf_name, binary_name))) v6filter_inst.assert_has_calls(calls) def test_create_firewall_group_no_rules(self): apply_list = self._fake_apply_list() first_ri = apply_list[0][0] firewall = self._fake_firewall_no_rule() self.firewall.create_firewall_group('legacy', apply_list, firewall) binary_name = fwaas.iptables_manager.binary_name dropped = '%s-dropped' % binary_name invalid_rule = '-m state --state INVALID -j %s' % dropped est_rule = '-m state --state RELATED,ESTABLISHED -j ACCEPT' for ip_version in (4, 6): ingress_chain = ('iv%s%s' % (ip_version, firewall['id'])) egress_chain = ('ov%s%s' % (ip_version, firewall['id'])) calls = [mock.call.remove_chain( 'iv%sfake-fw-uuid' % ip_version), mock.call.remove_chain( 'ov%sfake-fw-uuid' % ip_version), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule( 'fwaas-default-policy', '-j %s' % dropped), mock.call.add_chain(ingress_chain), mock.call.add_rule(ingress_chain, invalid_rule), mock.call.add_rule(ingress_chain, est_rule), mock.call.add_chain(egress_chain), mock.call.add_rule(egress_chain, invalid_rule), mock.call.add_rule(egress_chain, est_rule)] for port_id in FAKE_PORT_IDS: for direction in ['o', 'i']: mock.call.add_rule('FORWARD', '-%s qr-%s -j %s-fwaas-defau' % (port_id, direction, binary_name)) if ip_version == 4: v4filter_inst = first_ri.iptables_manager.ipv4['filter'] v4filter_inst.assert_has_calls(calls) else: v6filter_inst = first_ri.iptables_manager.ipv6['filter'] v6filter_inst.assert_has_calls(calls) def test_create_firewall_group_with_rules(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group) def test_create_firewall_group_with_rules_v6(self): self._setup_firewall_with_rules_v6(self.firewall.create_firewall_group) def test_create_firewall_group_with_rules_without_distributed_attr(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, distributed=None) def test_create_firewall_group_with_rules_two_routers(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, router_count=2) def test_update_firewall_group_with_rules(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group) def test_update_firewall_group_with_rules_v6(self): self._setup_firewall_with_rules_v6(self.firewall.update_firewall_group) def test_update_firewall_group_with_rules_without_distributed_attr(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group, distributed=None) def _test_delete_firewall_group(self, distributed=False): apply_list = self._fake_apply_list(distributed=distributed) first_ri = apply_list[0][0] firewall = self._fake_firewall_no_rule() self.firewall.delete_firewall_group('legacy', apply_list, firewall) ingress_chain = 'iv4%s' % firewall['id'] egress_chain = 'ov4%s' % firewall['id'] calls = [mock.call.remove_chain(ingress_chain), mock.call.remove_chain(egress_chain), mock.call.remove_chain('fwaas-default-policy')] first_ri.iptables_manager.ipv4['filter'].assert_has_calls(calls) def test_delete_firewall_group(self): self._test_delete_firewall_group() def test_delete_firewall_group_without_distributed_attr(self): self._test_delete_firewall_group(distributed=None) def test_create_firewall_group_with_admin_down(self): apply_list = self._fake_apply_list() first_ri = apply_list[0][0] rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall_with_admin_down(rule_list) binary_name = fwaas.iptables_manager.binary_name dropped = '%s-dropped' % binary_name self.firewall.create_firewall_group('legacy', apply_list, firewall) calls = [mock.call.remove_chain('iv4fake-fw-uuid'), mock.call.remove_chain('ov4fake-fw-uuid'), mock.call.remove_chain('fwaas-default-policy'), mock.call.add_chain('fwaas-default-policy'), mock.call.add_rule('fwaas-default-policy', '-j %s' % dropped)] first_ri.iptables_manager.ipv4['filter'].assert_has_calls(calls) def test_create_firewall_group_with_rules_dvr_snat(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, distributed=True, distributed_mode='dvr_snat') def test_update_firewall_group_with_rules_dvr_snat(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group, distributed=True, distributed_mode='dvr_snat') def test_create_firewall_group_with_rules_dvr(self): self._setup_firewall_with_rules(self.firewall.create_firewall_group, distributed=True, distributed_mode='dvr') def test_update_firewall_group_with_rules_dvr(self): self._setup_firewall_with_rules(self.firewall.update_firewall_group, distributed=True, distributed_mode='dvr') def test_remove_conntrack_new_firewall(self): apply_list = self._fake_apply_list() firewall = self._fake_firewall_no_rule() self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace calls = [mock.call(namespace)] self.firewall.conntrack.flush_entries.assert_has_calls(calls) def test_remove_conntrack_inserted_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) self.firewall.pre_firewall = dict(firewall) insert_rule = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'icmp', 'id': 'fake-fw-rule'} rule_list.insert(2, insert_rule) firewall = self._fake_firewall(rule_list) self.firewall.update_firewall_group(FW_LEGACY, apply_list, firewall) rules_changed = [ {'destination_port': '23', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'destination_port': '23', 'position': '3', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'} ] * 2 # Egress and ingress rule lists rules_inserted = [ {'id': 'fake-fw-rule', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'deny', 'position': '2'} ] * 2 # Egress and ingress rule lists for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace self.firewall.conntrack.delete_entries.assert_called_once_with( rules_changed + rules_inserted, namespace ) def test_remove_conntrack_removed_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) self.firewall.pre_firewall = dict(firewall) remove_rule = rule_list[1] rule_list.remove(remove_rule) firewall = self._fake_firewall(rule_list) self.firewall.update_firewall_group(FW_LEGACY, apply_list, firewall) rules_changed = [ {'destination_port': '23', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'destination_port': '23', 'position': '1', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'} ] * 2 # Egress and ingress rule lists rules_removed = [ {'enabled': True, 'position': '1', 'protocol': 'tcp', 'id': 'fake-fw-rule2', 'ip_version': 4, 'action': 'deny', 'destination_port': '22'} ] * 2 # Egress and ingress rule lists for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace self.firewall.conntrack.delete_entries.assert_called_once_with( rules_changed + rules_removed, namespace ) def test_remove_conntrack_changed_rule(self): apply_list = self._fake_apply_list() rule_list = self._fake_rules_v4(FAKE_FW_ID, apply_list) firewall = self._fake_firewall(rule_list) self.firewall.create_firewall_group(FW_LEGACY, apply_list, firewall) income_rule = {'enabled': True, 'action': 'deny', 'ip_version': 4, 'protocol': 'tcp', 'id': 'fake-fw-rule3'} rule_list[2] = income_rule firewall = self._fake_firewall(rule_list) self.firewall.update_firewall_group(FW_LEGACY, apply_list, firewall) rules_changed = [ {'id': 'fake-fw-rule3', 'enabled': True, 'action': 'reject', 'position': '2', 'destination_port': '23', 'ip_version': 4, 'protocol': 'tcp'}, {'position': '2', 'enabled': True, 'action': 'deny', 'id': 'fake-fw-rule3', 'ip_version': 4, 'protocol': 'tcp'} ] * 2 # Egress and ingress rule lists for router_info_inst, port_ids in apply_list: namespace = router_info_inst.iptables_manager.namespace self.firewall.conntrack.delete_entries.assert_called_once_with( rules_changed, namespace ) ././@PaxHeader0000000000000000000000000000023500000000000011455 xustar0000000000000000135 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_legacy_conntrack.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000001477100000000000034262 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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 testtools from neutron.tests import base from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux \ import legacy_conntrack from neutron_lib import constants FW_RULES = [ {'position': '1', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule1'}, {'source_port': '0:10', 'destination_port': '0:10', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule2'}, {'source_port': '0:10', 'destination_port': '0:20', 'position': '3', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'source_port': '1', 'destination_port': '0:10', 'position': '4', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, {'source_port': '0:10', 'destination_port': None, 'position': '5', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, {'source_port': '1', 'destination_port': '3', 'position': '6', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule6'}, {'source_port': '1', 'destination_port': '2', 'position': '7', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule7'}, ] ICMP_ENTRY = (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234') TCP_ENTRY = (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') UDP_ENTRY = (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') CONNTRACK_LIST = '''\ icmp 1 27 src=1.1.1.1 dst=2.2.2.2 type=8 code=0 id=18127 src=2.2.2.2 dst=1.1.1.1 type=0 code=0 id=18127 mark=0 use=1 tcp 6 88 SYN_SENT src=1.1.1.1 dst=2.2.2.2 sport=36567 dport=5000 [UNREPLIED] src=2.2.2.2 dst=1.1.1.1 sport=5000 dport=36567 mark=0 use=1 unknown 2 551 src=0.0.0.0 dst=224.0.0.1 [UNREPLIED] src=224.0.0.1 dst=0.0.0.0 mark=0 use=1 udp 17 28 src=0.0.0.0 dst=255.255.255.255 sport=68 dport=67 [UNREPLIED] src=255.255.255.255 dst=0.0.0.0 sport=67 dport=68 mark=0 use=1 ''' # noqa ROUTER_NAMESPACE = 'qrouter-fake-namespace' class ConntrackLegacyTestCase(base.BaseTestCase): def setUp(self): super(ConntrackLegacyTestCase, self).setUp() self.utils_exec = mock.Mock() self.conntrack_driver = legacy_conntrack.ConntrackLegacy() self.conntrack_driver.initialize(execute=self.utils_exec) def test_normalize_filters_tuple(self): self.assertEqual( (4, 'udp', [], [], '', ''), legacy_conntrack.normalize_filters_tuple( (4, 'udp', [], [], None, None))) self.assertEqual( (4, '', [], [], '', ''), legacy_conntrack.normalize_filters_tuple( (4, None, [], [], None, None))) def test_excecute_command_failed(self): with testtools.ExpectedException(RuntimeError): self.conntrack_driver._execute_command(['fake', 'command']) raise RuntimeError("Failed execute conntrack command fake command") def test_flush_entries(self): self.conntrack_driver.flush_entries(ROUTER_NAMESPACE) self.utils_exec.assert_called_with( ['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True, privsep_exec=True) def test_list_entries(self): def get_contrack_entries(conntrack_cmd): if 'ipv' + str(constants.IP_VERSION_4) in conntrack_cmd: return CONNTRACK_LIST return '' self.conntrack_driver._execute_command = mock.Mock( side_effect=get_contrack_entries) entries = self.conntrack_driver.list_entries(ROUTER_NAMESPACE) protocols = set([entry[1] for entry in entries]) supported_protocols = set(legacy_conntrack.ATTR_POSITIONS.keys()) self.assertTrue(protocols.issubset(supported_protocols)) def test_delete_entries(self): list_entries_mock = mock.patch( 'neutron_fwaas.services.firewall.service_drivers.agents.drivers.' 'linux.legacy_conntrack.ConntrackLegacy.list_entries') self.list_entries = list_entries_mock.start() self.conntrack_driver.list_entries.return_value = [ ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) calls = [ mock.call(['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D', '-f', 'ipv4', '-p', 'icmp', '--icmp-type', 8, '--icmp-code', 0, '-s', '1.1.1.1', '-d', '2.2.2.2', '--icmp-id', '1234'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True, privsep_exec=True), mock.call(['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D', '-f', 'ipv4', '-p', 'tcp', '--sport', 1, '--dport', 2, '-s', '1.1.1.1', '-d', '2.2.2.2'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True, privsep_exec=True), mock.call(['ip', 'netns', 'exec', ROUTER_NAMESPACE, 'conntrack', '-D', '-f', 'ipv4', '-p', 'udp', '--sport', 1, '--dport', 2, '-s', '1.1.1.1', '-d', '2.2.2.2'], check_exit_code=True, extra_ok_codes=[1], run_as_root=True, privsep_exec=True), ] self.utils_exec.assert_has_calls(calls) ././@PaxHeader0000000000000000000000000000023600000000000011456 xustar0000000000000000136 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_netlink_conntrack.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux0000664000175000017500000002434600000000000034261 0ustar00zuulzuul00000000000000# Copyright (c) 2017 Fujitsu Limited # 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_fwaas.services.firewall.service_drivers.agents.drivers.linux \ import netlink_conntrack from neutron_fwaas.tests import base FW_RULES = [ {'position': '1', 'protocol': 'icmp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule1'}, {'source_port': '0:10', 'destination_port': '0:10', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule2'}, {'source_port': '0:10', 'destination_port': '0:20', 'position': '3', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule3'}, {'source_port': None, 'destination_port': '0:10', 'position': '2', 'protocol': 'tcp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, {'source_port': '0:10', 'destination_port': None, 'position': '3', 'protocol': 'udp', 'ip_version': 4, 'enabled': True, 'action': 'reject', 'id': 'fake-fw-rule5'}, ] ICMP_ENTRY = (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234') TCP_ENTRY = (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') UDP_ENTRY = (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') ROUTER_NAMESPACE = 'qrouter-fake-namespace' class ConntrackNetlinkTestCase(base.BaseTestCase): def setUp(self): super(ConntrackNetlinkTestCase, self).setUp() self.conntrack_driver = netlink_conntrack.ConntrackNetlink() self.conntrack_driver.initialize() nl_flush_entries = mock.patch('neutron_fwaas.privileged.' 'netlink_lib.flush_entries') self.flush_entries = nl_flush_entries.start() nl_list_entries = mock.patch('neutron_fwaas.privileged.' 'netlink_lib.list_entries') self.list_entries = nl_list_entries.start() nl_delete_entries = mock.patch('neutron_fwaas.privileged.' 'netlink_lib.delete_entries') self.delete_entries = nl_delete_entries.start() def test_flush_entries(self): self.conntrack_driver.flush_entries(ROUTER_NAMESPACE) self.flush_entries.assert_called_with(ROUTER_NAMESPACE) def test_delete_with_empty_conntrack_entries(self): self.list_entries.return_value = [] self.conntrack_driver.delete_entries([], ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_not_called() def test_delete_icmp_entry(self): """Testing delete an icmp entry The icmp entry can be deleted if there is an icmp conntrack entry matched with an icmp firewall rule. The information passing to nl_lib.kill_entry will include: (ipversion, protocol, icmp_type, icmp_code, src_address, dst_addres, icmp_ip) """ self.list_entries.return_value = [ICMP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with([(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234')], ROUTER_NAMESPACE) def test_delete_tcp_entry(self): """Testing delete a tcp entry The tcp entry can be deleted if there is a tcp conntrack entry matched with a tcp firewall rule. The information passing to nl_lib.kill_entry will include: (ipversion, protocol, src_port, dst_port, src_address, dst_addres) """ self.list_entries.return_value = [TCP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with( [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2')], ROUTER_NAMESPACE) def test_delete_udp_entry(self): """Testing delete an udp entry The udp entry can be deleted if there is an udp conntrack entry matched with an udp firewall rule. The information passing to nl_lib.kill_entry will include: (ipversion, protocol, src_port, dst_port, src_address, dst_addres) """ self.list_entries.return_value = [UDP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with( [(4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2')], ROUTER_NAMESPACE) def test_delete_multiple_entries(self): self.list_entries.return_value = [ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY] self.conntrack_driver.delete_entries(FW_RULES, ROUTER_NAMESPACE) self.list_entries.assert_called_with(ROUTER_NAMESPACE) self.delete_entries.assert_called_with( [(4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234'), (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2')], ROUTER_NAMESPACE) def _test_entry_to_delete(self, rule_filter, entry, expect_result): is_entry_to_delete = ( self.conntrack_driver._compare_entry_and_rule(rule_filter, entry)) self.assertEqual(expect_result, is_entry_to_delete) def test_icmp_entry_match_rule(self): entry = (4, 'icmp', 8, 0, '1.1.1.1', '2.2.2.2', '1234') rule_filter = (4, 'icmp', None, None) self._test_entry_to_delete(rule_filter, entry, 0) def test_tcp_entry_match_rule(self): entry = (4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2') rule_filters = [(4, 'tcp', None, None), (4, 'tcp', [1], None), (4, 'tcp', None, [2]), (4, 'tcp', [1], [2]), (4, 'tcp', ['0', '10'], ['0', '10']), ] for rule_filter in rule_filters: self._test_entry_to_delete(rule_filter, entry, 0) def test_udp_entry_match_rule(self): entry = (4, 'udp', 1, 2, '1.1.1.1', '2.2.2.2') rule_filters = [(4, 'udp', None, None), (4, 'udp', [1], None), (4, 'udp', None, [2]), (4, 'udp', [1], [2]), (4, 'udp', ['0', '10'], ['0', '10']), ] for rule_filter in rule_filters: self._test_entry_to_delete(rule_filter, entry, 0) def test_entry_unmatch_rule(self): wrong_ipv = [(4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2'), (6, 'tcp', None, None), -1] wrong_proto = [(4, 'tcp', '1', '2', '1.1.1.1', '2.2.2.2'), (4, 'udp', None, None), -1] not_in_sport_range = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'tcp', ['2', '100'], [2]), -1] not_in_dport_range = [(4, 'tcp', 1, 2, '1.1.1.1', '2.2.2.2'), (4, 'tcp', [1], ['3', '100']), -1] for entry, rule_filter, expect in [ wrong_ipv, wrong_proto, not_in_sport_range, not_in_dport_range]: self._test_entry_to_delete(rule_filter, entry, expect) def test_get_filter_from_rules(self): fw_rule_icmp = FW_RULES[0] fw_rule_port_range = FW_RULES[1] fw_rule_dest_port = FW_RULES[3] fw_rule_source_port = FW_RULES[4] # filter format: # ('ip_version', 'protocol', 'source_port', 'destination_port', # 'source_ip_address', 'destination_ip_address') expected_icmp_filter = (4, 'icmp', [], [], [], []) expected_port_range_filter = (4, 'tcp', ['0', '10'], ['0', '10'], [], []) expected_dest_port_filter = (4, 'tcp', [], ['0', '10'], [], []) expected_source_port_filter = (4, 'udp', ['0', '10'], [], [], []) actual_icmp_filter = self.conntrack_driver._get_filter_from_rule( fw_rule_icmp) actual_port_range_filter = \ self.conntrack_driver._get_filter_from_rule(fw_rule_port_range) actual_dest_port_filter = \ self.conntrack_driver._get_filter_from_rule(fw_rule_dest_port) actual_source_port_filter = \ self.conntrack_driver._get_filter_from_rule(fw_rule_source_port) self.assertEqual(expected_icmp_filter, actual_icmp_filter) self.assertEqual(expected_port_range_filter, actual_port_range_filter) self.assertEqual(expected_dest_port_filter, actual_dest_port_filter) self.assertEqual(expected_source_port_filter, actual_source_port_filter) def test_get_entries_to_delete(self): rule_filters = sorted( [(4, 'tcp', ['0', '10'], ['1', '10']), (4, 'udp', ['0', '10'], ['0', '10']), (4, 'icmp', None, None)]) TCP_ENTRY_IN_RANGE = (4, 'tcp', 2, 3, '1.1.1.1', '2.2.2.2') TCP_ENTRY_OUT_RANGE = (4, 'tcp', 22, 100, '1.1.1.1', '2.2.2.2') UDP_ENTRY_IN_RANGE = (4, 'udp', 3, 4, '1.1.1.1', '2.2.2.2') UDP_ENTRY_OUT_RANGE = (4, 'udp', 100, 200, '1.1.1.1', '2.2.2.2') self.list_entries.return_value = sorted( [ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY, TCP_ENTRY_IN_RANGE, TCP_ENTRY_OUT_RANGE, UDP_ENTRY_IN_RANGE, UDP_ENTRY_OUT_RANGE]) expected_delete_entries = sorted( [ICMP_ENTRY, TCP_ENTRY, UDP_ENTRY, TCP_ENTRY_IN_RANGE, UDP_ENTRY_IN_RANGE]) actual_delete_entries = self.conntrack_driver._get_entries_to_delete( rule_filters, self.list_entries()) self.assertEqual(expected_delete_entries, actual_delete_entries) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/0000775000175000017500000000000000000000000032025 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/__init__.p0000664000175000017500000000000000000000000033733 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/fake_data.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/fake_data.0000664000175000017500000001224600000000000033732 0ustar00zuulzuul00000000000000# Copyright 2017 FUJITSU LIMITED # All Rights Reserved # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. # import copy from unittest import mock from neutron_lib import constants as nl_consts from oslo_utils import uuidutils TENANT_UUID = uuidutils.generate_uuid() TENANT_ID = TENANT_UUID PROJECT_ID = TENANT_UUID NETWORK_ID = uuidutils.generate_uuid() SUBNET_ID = uuidutils.generate_uuid() DEVICE_ID = uuidutils.generate_uuid() PORT1 = uuidutils.generate_uuid() PORT2 = uuidutils.generate_uuid() PORT3 = uuidutils.generate_uuid() PORT4 = uuidutils.generate_uuid() HOST = 'fake_host' class FakeFWaaSL2Agent(object): def __init__(self): super(FakeFWaaSL2Agent, self).__init__() def create(self, resource, attrs=None, minimal=False): """Create a fake fwaas v2 resources :param resource: A dictionary with all attributes :type resource: string :param attrs: A dictionary of each attribute you need to modify :type attrs: dictionary :param minimal: True if minimal port_detail is necessary otherwise False :type minimal: boolean :return: A OrderedDict faking the fwaas v2 resource """ target = getattr(self, "_" + resource) return copy.deepcopy(target(attrs=attrs, minimal=minimal)) def _fwg(self, **kwargs): fwg = { 'id': uuidutils.generate_uuid(), 'name': 'my-group-' + uuidutils.generate_uuid(), 'ingress_firewall_policy_id': uuidutils.generate_uuid(), 'egress_firewall_policy_id': uuidutils.generate_uuid(), 'description': 'my firewall group', 'status': nl_consts.PENDING_CREATE, 'ports': [PORT3, PORT4], 'admin_state_up': True, 'shared': False, 'tenant_id': TENANT_ID, 'project_id': PROJECT_ID } attrs = kwargs.get('attrs', None) if attrs: fwg.update(attrs) return fwg def _fwg_with_rule(self, **kwargs): fwg_with_rule = self.create('fwg', attrs={'ports': [PORT1, PORT2]}) rules = { 'ingress_rule_list': [mock.Mock()], 'egress_rule_list': [mock.Mock()], 'add-port-ids': [PORT1], 'del-port-ids': [PORT2], 'port_details': { PORT1: { 'device': uuidutils.generate_uuid(), 'device_owner': 'compute:nova', 'host': HOST, 'network_id': NETWORK_ID, 'fixed_ips': [ {'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.5'}], 'allowed_address_pairs': [], 'port_security_enabled': True, 'id': PORT1 }, PORT2: { 'device': uuidutils.generate_uuid(), 'device_owner': 'compute:nova', 'host': HOST, 'network_id': NETWORK_ID, 'fixed_ips': [ {'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.6'}], 'allowed_address_pairs': [], 'port_security_enabled': True, 'id': PORT2 } }, } fwg_with_rule.update(rules) if kwargs.get('minimal', None): fwg_with_rule.update({'ports': []}) fwg_with_rule.update({'add-port-ids': []}) fwg_with_rule.update({'del-port-ids': []}) fwg_with_rule.update({'port_details': {}}) attrs = kwargs.get('attrs', None) if attrs: fwg_with_rule.update(attrs) return fwg_with_rule def _port(self, **kwargs): if kwargs.get('minimal', None): return {'port_id': uuidutils.generate_uuid()} port_detail = { 'profile': {}, 'network_qos_policy_id': None, 'qos_policy_id': None, 'allowed_address_pairs': [], 'admin_state_up': True, 'network_id': NETWORK_ID, 'segmentation_id': None, 'fixed_ips': [ {'subnet_id': SUBNET_ID, 'ip_address': '172.24.4.5'}], 'vif_port': mock.Mock(), 'device_owner': 'compute:node', 'physical_network': 'physnet', 'mac_address': 'fa:16:3e:8a:80:2b', 'device': DEVICE_ID, 'port_security_enabled': True, 'port_id': uuidutils.generate_uuid(), 'network_type': 'flat', 'security_groups': [] } attrs = kwargs.get('attrs', None) if attrs: port_detail.update(attrs) return port_detail ././@PaxHeader0000000000000000000000000000021200000000000011450 xustar0000000000000000116 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/test_fwaas_v2.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/test_fwaas0000664000175000017500000007333000000000000034116 0ustar00zuulzuul00000000000000# Copyright 2017 Cisco Systems # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from unittest import mock from neutron_lib import constants as nl_consts from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as f_exc from oslo_config import cfg from neutron_fwaas.common import fwaas_constants as consts from neutron_fwaas.services.firewall.service_drivers.agents.l2 import fwaas_v2 from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.services.firewall.service_drivers.agents.l2\ import fake_data class TestFWaasV2AgentExtensionBase(base.BaseTestCase): def setUp(self): super(TestFWaasV2AgentExtensionBase, self).setUp() self.fake = fake_data.FakeFWaaSL2Agent() self.port = self.fake.create('port') self.port_minimal = self.fake.create('port', minimal=True) self.fwg = self.fake.create('fwg') self.fwg_with_rule = self.fake.create('fwg_with_rule') self.port_id = self.port['port_id'] self.fwg_id = self.fwg['id'] self.host = fake_data.HOST self.ctx = context.get_admin_context() self.l2 = fwaas_v2.FWaaSV2AgentExtension() self.l2.consume_api(mock.Mock()) self._driver_mock = mock.patch( 'neutron.manager.NeutronManager.load_class_for_provider') self.driver = self._driver_mock.start() self.addCleanup(self._driver_mock.stop) self.l2.initialize(None, 'ovs') self.l2.vlan_manager = mock.Mock() self.conf = cfg.ConfigOpts() self.l2.fwg_map = mock.Mock() self.l2.conf.host = self.host self.rpc = self.l2.plugin_rpc class TestFWaasV2AgentExtension(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestFWaasV2AgentExtension, self).setUp() cfg.CONF.set_override('firewall_l2_driver', 'ovs', group='fwaas') def test_initialize(self): with mock.patch('neutron_lib.rpc.Connection') as conn: self.l2.initialize(None, 'ovs') self.driver.assert_called_with('neutron.agent.l2.firewall_drivers', 'ovs') conn.assert_called_with() self.l2.conn.create_consumer.assert_called_with( consts.FW_AGENT, [self.l2], fanout=False) self.l2.conn.consume_in_threads.assert_called_with() class TestHandlePort(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestHandlePort, self).setUp() self.rpc.get_firewall_group_for_port = mock.Mock( return_value=self.fwg) self.l2._compute_status = mock.Mock(return_value=nl_consts.ACTIVE) self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._send_fwg_status = mock.Mock() self.ctx = context.get_admin_context() self.l2._add_rule_for_trusted_port = mock.Mock() def test_normal(self): self.l2.fwg_map.get_port_fwg.return_value = None self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_called_once_with( self.ctx, self.port['port_id']) self.l2._apply_fwg_rules.assert_called_once_with(self.fwg, [self.port]) self.l2._compute_status.assert_called_once_with( self.fwg, True, event=consts.HANDLE_PORT) self.l2.fwg_map.set_port_fwg.assert_called_once_with(self.port, self.fwg) self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg_id=self.fwg['id'], status=nl_consts.ACTIVE, host=self.l2.conf.host) def test_non_layer2_port(self): self.port['device_owner'] = 'network:router_gateway' self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_not_called() self.l2._apply_fwg_rules.assert_not_called() self.l2._compute_status.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_not_called() def test_no_fwg_is_asossicate_to_port(self): self.l2.fwg_map.get_port_fwg.return_value = None self.rpc.get_firewall_group_for_port.return_value = None self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_called_once_with( self.ctx, self.port['port_id']) self.l2._apply_fwg_rules.assert_not_called() self.l2._compute_status.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_not_called() def test_port_already_apply_fwg(self): self.l2.fwg_map.get_port_fwg.return_value = self.fwg self.l2.handle_port(self.ctx, self.port) self.rpc.get_firewall_group_for_port.assert_not_called() self.l2._apply_fwg_rules.assert_not_called() self.l2._compute_status.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_not_called() def test_trusted_port(self): self.l2.fwg_map.get_port.return_value = None self.port['device_owner'] = 'network:foo' self.l2.handle_port(self.ctx, self.port) self.l2._add_rule_for_trusted_port.assert_called_once_with(self.port) self.l2.fwg_map.set_port.assert_called_once_with(self.port) self.rpc.get_firewall_group_for_port.assert_not_called() def test_trusted_port_registered_map(self): self.port['device_owner'] = 'network:dhcp' self.l2.fwg_map.get_port.return_value = self.port self.l2.handle_port(self.ctx, self.port) self.l2._add_rule_for_trusted_port.assert_not_called() self.l2.fwg_map.set_port.assert_not_called() self.rpc.get_firewall_group_for_port.assert_not_called() class TestDeletePort(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestDeletePort, self).setUp() self.l2._compute_status = mock.Mock(return_value=nl_consts.ACTIVE) self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._send_fwg_status = mock.Mock() self.l2._delete_rule_for_trusted_port = mock.Mock() self.l2.fwg_map.get_port_fwg = mock.Mock(return_value=self.fwg) self.l2.fwg_map.set_fwg = mock.Mock() self.l2.fwg_map.get_port = mock.Mock(return_value=self.port) self.l2.fwg_map.remove_port = mock.Mock() def test_include_vif_port_attribute(self): self.port_minimal.update({'vif_port': None}) self.l2.fwg_map.get_port_fwg.return_value = None self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_not_called() self.l2._apply_fwg_rules.assert_not_called() def test_port_belongs_to_fwg(self): expected_ports = self.fwg['ports'] self.fwg['ports'].append(self.port['port_id']) self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port) self.l2._apply_fwg_rules.assert_called_once_with( self.fwg, [self.port], event=consts.DELETE_FWG) # 'port_id' has been removed from 'ports' self.assertEqual(expected_ports, self.fwg['ports']) self.l2.fwg_map.set_fwg.assert_called_once_with(self.fwg) def test_port_belongs_to_no_fwg(self): expected_ports = self.fwg['ports'] self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port) self.l2._apply_fwg_rules.assert_called_once_with( self.fwg, [self.port], event=consts.DELETE_FWG) # 'ports' not changed during delete_port() self.assertEqual(expected_ports, self.fwg['ports']) self.l2.fwg_map.set_fwg.assert_called_once_with(self.fwg) def test_non_layer2_port(self): self.port['device_owner'] = 'network:router_gateway' self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_not_called() def test_cannot_get_fwg_from_port(self): self.l2.fwg_map.get_port_fwg.return_value = None self.l2.delete_port(self.ctx, self.port_minimal) self.l2.fwg_map.get_port_fwg.assert_called_once_with(self.port) self.l2._apply_fwg_rules.assert_not_called() def test_trusted_port_with_map(self): self.port['device_owner'] = 'network:dhcp' self.l2.fwg_map.get_port.return_value = self.port self.l2.delete_port(self.ctx, self.port_minimal) self.l2._delete_rule_for_trusted_port.assert_called_once_with( self.port) self.l2.fwg_map.remove_port.assert_called_once_with(self.port) class TestCreateFirewallGroup(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestCreateFirewallGroup, self).setUp() self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._compute_status = mock.Mock(return_value='ACTIVE') self.l2._send_fwg_status = mock.Mock() def test_create_event_is_create(self): fwg = self.fwg_with_rule fwg['ports'] = [fake_data.PORT1] ports = [fwg['port_details'][fake_data.PORT1]] self.l2._create_firewall_group( self.ctx, fwg, self.host, event=consts.CREATE_FWG) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, consts.CREATE_FWG) self.l2._compute_status.assert_called_once_with( fwg, True, consts.CREATE_FWG) def test_create_event_is_not_create(self): fwg = self.fwg_with_rule fwg['ports'] = [fake_data.PORT1] ports = [fwg['port_details'][fake_data.PORT1]] self.l2._create_firewall_group( self.ctx, fwg, self.host, event=consts.UPDATE_FWG) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, consts.UPDATE_FWG) def test_create_with_port(self): fwg = self.fwg_with_rule ports = [fwg['port_details'][fake_data.PORT1]] self.l2.create_firewall_group(self.ctx, fwg, self.host) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, consts.CREATE_FWG) for idx, args in enumerate(self.l2._compute_status.call_args_list): self.assertEqual(fwg, args[0][0]) self.assertEqual(True, args[0][1]) self.assertEqual(consts.CREATE_FWG, args[0][2]) for idx, args in enumerate(self.l2._send_fwg_status.call_args_list): self.assertEqual(self.ctx, args[0][0]) self.assertEqual(fwg['id'], args[0][1]) self.assertEqual('ACTIVE', args[0][2]) self.assertEqual(self.host, args[0][3]) def test_create_with_no_ports(self): self.fwg_with_rule['add-port-ids'] = [] self.assertIsNone(self.l2.create_firewall_group( self.ctx, self.fwg_with_rule, self.host)) self.l2._apply_fwg_rules.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_called_once_with( self.ctx, self.fwg_with_rule['id'], 'INACTIVE', self.host) def test_create_with_invalid_host(self): self.fwg_with_rule['port_details'][fake_data.PORT1]['host'] = 'invalid' self.l2.create_firewall_group(self.ctx, self.fwg_with_rule, self.host) self.l2._apply_fwg_rules.assert_not_called() self.l2._send_fwg_status.assert_called_once_with( self.ctx, self.fwg_with_rule['id'], 'INACTIVE', self.host) def test_illegal_create_with_no_l2_ports(self): fwg = { 'name': 'non-default', 'id': self.fwg_id, 'ports': [], 'add-port-ids': [self.port_id], 'admin_state_up': True, 'port_details': { self.port_id: { 'device_owner': 'network:router_interface' } } } self.l2.create_firewall_group(self.ctx, fwg, self.host) self.l2._apply_fwg_rules.assert_not_called() self.l2.fwg_map.set_port_fwg.assert_not_called() self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg['id'], 'INACTIVE', self.host) class TestDeleteFirewallGroup(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestDeleteFirewallGroup, self).setUp() self.l2._apply_fwg_rules = mock.Mock(return_value=True) self.l2._compute_status = mock.Mock(return_value='ACTIVE') self.l2._send_fwg_status = mock.Mock() self.rpc.firewall_group_deleted = mock.Mock() def test_delete_with_port(self): fwg = self.fwg_with_rule ports = [fwg['port_details'][fake_data.PORT2]] self.assertIsNone(self.l2.delete_firewall_group( self.ctx, self.fwg_with_rule, self.host)) self.l2._apply_fwg_rules.assert_called_once_with( fwg, ports, event=consts.DELETE_FWG) self.l2.fwg_map.remove_fwg.assert_called_once_with(fwg) for idx, args in enumerate(self.l2._compute_status.call_args_list): self.assertEqual(fwg, args[0][0]) self.assertEqual(True, args[0][2]) self.assertEqual({'event': consts.CREATE_FWG}, args[1]) for idx, args in enumerate(self.l2._send_fwg_status.call_args_list): self.assertEqual(self.ctx, args[0][0]) self.assertEqual(fwg['id'], args[0][1]) self.assertEqual('ACTIVE', args[0][2]) self.assertEqual(self.host, args[0][3]) def test_delete_with_no_ports(self): self.fwg_with_rule['del-port-ids'] = [] self.l2.delete_firewall_group(self.ctx, self.fwg_with_rule, self.host) self.l2._apply_fwg_rules.assert_not_called() def test_delete_with_no_l2_ports(self): self.fwg_with_rule['port_details'][fake_data.PORT2][ 'device_owner'] = 'network:router_interface' self.l2.delete_firewall_group(self.ctx, self.fwg_with_rule, self.host) self.l2._apply_fwg_rules.assert_not_called() def test_delete_with_exception(self): self.l2._delete_firewall_group = mock.Mock(side_effect=Exception) self.assertIsNone(self.l2.delete_firewall_group( self.ctx, self.fwg_with_rule, self.host)) def test_delete_event_is_update(self): self.l2._delete_firewall_group( self.ctx, self.fwg_with_rule, self.host, event=consts.UPDATE_FWG) self.l2.fwg_map.remove_fwg.assert_not_called() self.rpc.firewall_group_deleted.assert_not_called() self.l2._compute_status.assert_called_once_with( self.fwg_with_rule, True, consts.UPDATE_FWG) self.l2._send_fwg_status.assert_called_once_with( self.ctx, self.fwg_with_rule['id'], 'ACTIVE', self.host) class TestUpdateFirewallGroup(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestUpdateFirewallGroup, self).setUp() self.l2._delete_firewall_group = mock.Mock() self.l2._create_firewall_group = mock.Mock() self.l2._send_fwg_status = mock.Mock() def test_update(self): self.assertIsNone(self.l2.update_firewall_group( self.ctx, mock.ANY, self.host)) self.l2._delete_firewall_group.assert_called_once_with( self.ctx, mock.ANY, self.host, event=consts.UPDATE_FWG) self.l2._create_firewall_group.assert_called_once_with( self.ctx, mock.ANY, self.host, event=consts.UPDATE_FWG) def test_update_raised_in_delete_firewall_group(self): self.l2._delete_firewall_group.side_effect = Exception fwg = self.fwg_with_rule self.assertIsNone(self.l2.update_firewall_group( self.ctx, fwg, self.host)) self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg['id'], status='ERROR', host=self.host) def test_update_raised_in_create_firewall_group(self): self.l2._create_firewall_group.side_effect = Exception fwg = self.fwg_with_rule self.assertIsNone(self.l2.update_firewall_group( self.ctx, fwg, self.host)) self.l2._send_fwg_status.assert_called_once_with( self.ctx, fwg['id'], status='ERROR', host=self.host) class TestIsPortLayer2(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestIsPortLayer2, self).setUp() def test_vm_port(self): self.assertTrue(self.l2._is_port_layer2(self.port)) def test_not_vm_port(self): for device_owner in [nl_consts.DEVICE_OWNER_ROUTER_INTF, nl_consts.DEVICE_OWNER_ROUTER_GW, nl_consts.DEVICE_OWNER_DHCP, nl_consts.DEVICE_OWNER_DVR_INTERFACE, nl_consts.DEVICE_OWNER_AGENT_GW, nl_consts.DEVICE_OWNER_ROUTER_SNAT, 'unknown device_owner', '']: self.port['device_owner'] = device_owner self.assertFalse(self.l2._is_port_layer2(self.port)) def test_illegal_no_device_owner(self): del self.port['device_owner'] self.assertFalse(self.l2._is_port_layer2(self.port)) class TestComputeStatus(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestComputeStatus, self).setUp() self.ports = list(self.fwg_with_rule['port_details'].values()) def test_normal(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status(fwg, result)) def test_event_is_delete(self): result = True fwg = self.fwg_with_rule self.assertIsNone(self.l2._compute_status( fwg, result, consts.DELETE_FWG)) def test_event_is_update(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_update_and_has_last_port(self): result = True fwg = self.fake.create('fwg_with_rule', attrs={'last-port': False}) self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) fwg = self.fake.create('fwg_with_rule', attrs={'last-port': True}) self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_update_and_has_no_last_port_but_has_ports(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_update_and_has_no_last_port_and_ports(self): result = True fwg = self.fwg_with_rule fwg['ports'] = [] self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.UPDATE_FWG)) def test_event_is_create(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.CREATE_FWG)) def test_event_is_create_and_no_fwg_ports(self): result = True fwg = self.fwg_with_rule fwg['ports'] = [] self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.CREATE_FWG)) def test_event_is_handle_port(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.HANDLE_PORT)) def test_event_is_delete_port(self): result = True fwg = self.fwg_with_rule self.assertEqual('ACTIVE', self.l2._compute_status( fwg, result, consts.DELETE_PORT)) def test_event_is_delete_port_and_no_fwg_ports(self): result = True fwg = self.fwg_with_rule fwg['ports'] = [] self.assertEqual('INACTIVE', self.l2._compute_status( fwg, result, consts.DELETE_PORT)) def test_driver_result_is_false(self): result = False fwg = self.fwg_with_rule self.assertEqual('ERROR', self.l2._compute_status( fwg, result)) def test_admin_state_up_is_false(self): result = True self.fwg_with_rule['admin_state_up'] = False self.assertEqual('DOWN', self.l2._compute_status( self.fwg_with_rule, self.ports, result)) def test_active_inactive_patterns(self): result = True fwg = self.fwg_with_rule # Case1: ingress/egress_firewall_policy_id # Case2: ports --> already tested at above cases expect_and_attrs = [ ('INACTIVE', ('ingress_firewall_policy_id', 'egress_firewall_policy_id')), ('ACTIVE', ('ingress_firewall_policy_id',)), ('ACTIVE', ('egress_firewall_policy_id',)), ] for attr in expect_and_attrs: fwg = self.fake.create('fwg_with_rule') expect = attr[0] for p in attr[1]: fwg[p] = None self.assertEqual(expect, self.l2._compute_status(fwg, result)) class TestApplyFwgRules(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestApplyFwgRules, self).setUp() class DummyVlan(object): def __init__(self, vlan=None): self.vlan = vlan self.l2.vlan_manager.get.return_value = DummyVlan(vlan='999') def test_event_is_create(self): fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertTrue(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.CREATE_FWG)) self.l2.driver.create_firewall_group.assert_called_once_with( driver_ports, self.fwg_with_rule) self.l2.driver.delete_firewall_group.assert_not_called() self.l2.driver.update_firewall_group.assert_not_called() def test_event_is_update(self): fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertTrue(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.UPDATE_FWG)) self.l2.driver.update_firewall_group.assert_called_once_with( driver_ports, self.fwg_with_rule) def test_event_is_delete(self): fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertTrue(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.DELETE_FWG)) self.l2.driver.delete_firewall_group.assert_called_once_with( fwg_ports, self.fwg_with_rule) def test_raised_in_driver(self): self.l2.driver.delete_firewall_group.side_effect = \ f_exc.FirewallInternalDriverError(driver='ovs firewall') fwg_ports = [self.fwg_with_rule['port_details'][fake_data.PORT1]] driver_ports = copy.deepcopy(fwg_ports) driver_ports[0].update({'lvlan': 999}) self.assertFalse(self.l2._apply_fwg_rules( self.fwg_with_rule, fwg_ports, event=consts.DELETE_FWG)) self.l2.driver.delete_firewall_group.assert_called_once_with( fwg_ports, self.fwg_with_rule) class TestSendFwgStatus(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestSendFwgStatus, self).setUp() self.rpc.set_firewall_group_status = mock.Mock() def test_success(self): self.assertIsNone(self.l2._send_fwg_status( self.ctx, self.fwg_id, 'ACTIVE', self.host)) def test_failure(self): self.rpc.set_firewall_group_status.side_effect = Exception self.assertIsNone(self.l2._send_fwg_status( self.ctx, self.fwg_id, 'ACTIVE', self.host)) class TestAddLocalVlanToPorts(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestAddLocalVlanToPorts, self).setUp() class DummyVlan(object): def __init__(self, vlan=None): self.vlan = vlan self.l2.vlan_manager.get.return_value = DummyVlan(vlan='999') self.port_with_detail = { 'port_id': fake_data.PORT1, 'id': fake_data.PORT1, 'network_id': fake_data.NETWORK_ID, 'segmentation_id': 101, 'port_details': { fake_data.PORT1: { 'device': 'c12e5c1e-d68e-45bd-a2d3-1f2f32604e41', 'device_owner': 'compute:nova', 'host': self.host, 'network_id': fake_data.NETWORK_ID, 'fixed_ips': [ {'subnet_id': fake_data.SUBNET_ID, 'ip_address': '172.24.4.5'}], 'allowed_address_pairs': [], 'port_security_enabled': True, 'id': fake_data.PORT1 } } } def test_port_has_detail_and_port_id(self): del self.port_with_detail['id'] expect = [copy.deepcopy(self.port_with_detail)] expect[0].update({'lvlan': 999}) actual = self.l2._add_local_vlan_to_ports([self.port_with_detail]) self.l2.vlan_manager.get.assert_called_once_with( self.port_with_detail['network_id'], None) self.assertEqual(expect, actual) def test_port_has_detail_and_id(self): del self.port_with_detail['port_id'] expect = [copy.deepcopy(self.port_with_detail)] expect[0].update({'lvlan': 999}) actual = self.l2._add_local_vlan_to_ports([self.port_with_detail]) self.l2.vlan_manager.get.assert_called_once_with( self.port_with_detail['network_id'], None) self.assertEqual(expect, actual) def test_port_has_no_detail(self): del self.port_with_detail['port_details'] expect = [copy.deepcopy(self.port_with_detail)] expect[0].update({'lvlan': 999}) actual = self.l2._add_local_vlan_to_ports([self.port_with_detail]) self.l2.vlan_manager.get.assert_called_once_with( self.port_with_detail['network_id'], self.port_with_detail['segmentation_id']) self.assertEqual(expect, actual) class TestFWaaSL2PluginApi(TestFWaasV2AgentExtensionBase): def setUp(self): super(TestFWaaSL2PluginApi, self).setUp() self.plugin = fwaas_v2.FWaaSL2PluginApi( consts.FIREWALL_PLUGIN, self.host) self.plugin.client = mock.Mock() self.cctxt = self.plugin.client.prepare() def test_get_firewall_group_for_port(self): self.plugin.get_firewall_group_for_port(self.ctx, mock.ANY) self.cctxt.call.assert_called_once_with( self.ctx, 'get_firewall_group_for_port', port_id=mock.ANY ) def test_set_firewall_group_status(self): self.plugin.set_firewall_group_status( self.ctx, self.fwg_id, 'ACTIVE', self.host) self.cctxt.call.assert_called_once_with( self.ctx, 'set_firewall_group_status', fwg_id=self.fwg_id, status='ACTIVE', host=self.host, ) def test_firewall_group_deleted(self): self.plugin.firewall_group_deleted(self.ctx, self.fwg_id, self.host) self.cctxt.call.assert_called_once_with( self.ctx, 'firewall_group_deleted', fwg_id=self.fwg_id, host=self.host, ) class TestPortFirewallGroupMap(base.BaseTestCase): def setUp(self): super(TestPortFirewallGroupMap, self).setUp() self.fake = fake_data.FakeFWaaSL2Agent() self.map = fwaas_v2.PortFirewallGroupMap() self.fwg = self.fake.create('fwg') self.fwg_id = self.fwg['id'] self.port = self.fake.create('port') self.fwg['ports'] = [] def test_set_and_get(self): self.map.set_fwg(self.fwg) self.assertEqual(self.fwg, self.map.get_fwg(self.fwg_id)) def test_set_and_get_port_fwg(self): port1 = self.port port2 = self.fake.create('port') self.map.set_port_fwg(port1, self.fwg) self.map.set_port_fwg(port2, self.fwg) self.assertEqual(self.fwg, self.map.get_port_fwg(port1)) self.assertEqual(self.fwg, self.map.get_port_fwg(port2)) self.assertIsNone(self.map.get_port_fwg('unknown')) def test_remove_port(self): port1 = self.port port2 = self.fake.create('port') self.map.set_port_fwg(port1, self.fwg) self.map.remove_port(port2) self.map.set_port_fwg(port2, self.fwg) self.map.remove_port(port1) self.assertIsNone(self.map.get_port(port1)) self.assertEqual([port2['port_id']], self.map.get_fwg(self.fwg_id)['ports']) self.map.remove_port(port2) self.assertIsNone(self.map.get_port(port2)) self.assertEqual([], self.map.get_fwg(self.fwg_id)['ports']) def test_remove_non_exist_port(self): port1 = self.port port2 = self.fake.create('port') self.map.set_port_fwg(port1, self.fwg) self.map.remove_port(port2) self.assertIsNone(self.map.get_port(port2)) def test_illegal_remove_port_no_relation_with_fwg(self): port1 = self.port port1_id = port1['port_id'] self.map.set_port_fwg(port1, self.fwg) self.map.port_fwg[port1_id] = None self.map.remove_port(port1) self.assertIsNone(self.map.get_port(port1)) def test_remove_fwg(self): self.map.set_fwg(self.fwg) self.assertEqual(self.fwg, self.map.get_fwg(self.fwg_id)) self.map.remove_fwg(self.fwg) self.assertIsNone(self.map.get_fwg(self.fwg_id)) def test_remove_fwg_non_exist(self): self.map.remove_fwg(self.fwg) self.assertIsNone(self.map.get_fwg(self.fwg_id)) ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3253515 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/0000775000175000017500000000000000000000000033705 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000021600000000000011454 xustar0000000000000000120 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/__init__.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/_0000664000175000017500000000000000000000000034034 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000023700000000000011457 xustar0000000000000000137 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent_v2.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/t0000664000175000017500000006644400000000000034111 0ustar00zuulzuul00000000000000# Copyright (c) 2016 # 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.agent.l3 import l3_agent_extension_api as l3_agent_api from neutron.agent.l3 import router_info from neutron.agent.linux import ip_lib from neutron.conf.agent.l3 import config as l3_config from neutron_lib import context from oslo_config import cfg from oslo_utils import uuidutils from neutron_fwaas.common import fwaas_constants from neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux \ import iptables_fwaas_v2 from neutron_fwaas.services.firewall.service_drivers.agents \ import firewall_agent_api from neutron_fwaas.services.firewall.service_drivers.agents.l3reference \ import firewall_l3_agent_v2 from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.services.firewall.service_drivers.agents \ import test_firewall_agent_api class FWaasHelper(object): def __init__(self): pass class FWaasAgent(firewall_l3_agent_v2.L3WithFWaaS, FWaasHelper): neutron_service_plugins = [] def add_router(self, context, data): pass def delete_router(self, context, data): pass def update_router(self, context, data): pass def _setup_test_agent_class(service_plugins): class FWaasTestAgent(firewall_l3_agent_v2.L3WithFWaaS, FWaasHelper): neutron_service_plugins = service_plugins def __init__(self, conf): self.event_observers = mock.Mock() self.conf = conf firewall_agent_api._check_required_agent_extension = mock.Mock() super(FWaasTestAgent, self).__init__(conf) def delete_router(self, context, data): pass return FWaasTestAgent class TestFWaaSL3AgentExtension(base.BaseTestCase): def setUp(self): super(TestFWaaSL3AgentExtension, self).setUp() self.conf = cfg.ConfigOpts() self.conf.register_opts(l3_config.OPTS) self.conf.register_opts(firewall_agent_api.FWaaSOpts, 'fwaas') self.conf.host = 'myhost' self.api = FWaasAgent(self.conf) self.api.agent_api = mock.Mock() self.api.fwaas_driver = test_firewall_agent_api.NoopFwaasDriverV2() self.adminContext = context.get_admin_context() self.context = mock.sentinel.context self.router_id = uuidutils.generate_uuid() self.agent_conf = mock.Mock() self.ri_kwargs = {'router': {'id': self.router_id, 'project_id': uuidutils.generate_uuid()}, 'agent_conf': self.agent_conf, 'interface_driver': mock.ANY, 'use_ipv6': mock.ANY } def test_fw_config_match(self): test_agent_class = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') with mock.patch('oslo_utils.importutils.import_object'): test_agent_class(cfg.CONF) def test_fw_config_mismatch_plugin_enabled_agent_disabled(self): self.skipTest('this is broken') test_agent_class = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', False, 'fwaas') self.assertRaises(SystemExit, test_agent_class, cfg.CONF) def test_fw_plugin_list_unavailable(self): test_agent_class = _setup_test_agent_class(None) cfg.CONF.set_override('enabled', False, 'fwaas') with mock.patch('oslo_utils.importutils.import_object'): test_agent_class(cfg.CONF) def test_create_firewall_group(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'add-port-ids': [1, 2]} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'create_firewall_group' ) as mock_driver_create_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_create_firewall_group.return_value = True self.api.create_firewall_group(self.context, firewall_group, host='host') mock_get_firewall_group_ports.assert_called_once_with(self.context, firewall_group) mock_get_in_ns_ports.assert_called assert mock_get_in_ns_ports mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_update_firewall_group_with_ports_added_and_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [1, 2, 3, 4], 'add-port-ids': [1, 2], 'del-port-ids': [3, 4], 'router_ids': [], 'last-port': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall_group' ) as mock_driver_delete_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_delete_firewall_group.return_value = True mock_driver_update_firewall_group.return_value = True calls = [mock.call(self.context, firewall_group, to_delete=True, require_new_plugin=True), mock.call(self.context, firewall_group)] self.api.update_firewall_group(self.context, firewall_group, host='host') self.assertEqual(mock_get_firewall_group_ports.call_args_list, calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_update_firewall_group_with_ports_added_and_admin_state_down(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': False, 'ports': [1, 2], 'add-port-ids': [1, 2], 'del-port-ids': [], 'router_ids': [], 'last-port': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') mock_get_firewall_group_ports.assert_called mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'DOWN') def test_update_firewall_group_with_all_ports_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [3, 4], 'add-port-ids': [], 'del-port-ids': [3, 4], 'last-port': True} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall_group' ) as mock_driver_delete_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_delete_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') calls = [ mock.call._get_firewall_group_ports( self.context, firewall_group, require_new_plugin=True, to_delete=True), mock.call._get_firewall_group_ports( self.context, firewall_group) ] mock_get_firewall_group_ports.assert_has_calls(calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'INACTIVE') def test_update_firewall_group_with_no_ports_added_or_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [], 'add-port-ids': [], 'del-port-ids': [], 'last-port': True} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') calls = [ mock.call._get_firewall_group_ports( self.context, firewall_group, require_new_plugin=True, to_delete=True), mock.call._get_firewall_group_ports( self.context, firewall_group) ] mock_get_firewall_group_ports.assert_has_calls(calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'INACTIVE') def test_update_firewall_group_with_only_ports_added(self): # This test is for bug/1634114 firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [1, 2], 'add-port-ids': ['1', '2'], 'del-port-ids': [], 'last-port': False } with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') calls = [ mock.call._get_firewall_group_ports( self.context, firewall_group, require_new_plugin=True, to_delete=True), mock.call._get_firewall_group_ports( self.context, firewall_group) ] mock_get_firewall_group_ports.assert_has_calls(calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_update_firewall_group_with_only_ports_removed(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [1, 2], 'add-port-ids': [], 'del-port-ids': ['1'], 'last-port': False } self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') calls = [ mock.call._get_firewall_group_ports( self.context, firewall_group, require_new_plugin=True, to_delete=True), mock.call._get_firewall_group_ports( self.context, firewall_group) ] mock_get_firewall_group_ports.assert_has_calls(calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_update_firewall_group_with_only_policies_added_or_deleted(self): firewall_group = {'id': 0, 'project_id': 1, 'tenant_id': 1, 'admin_state_up': True, 'ports': [1, 2], 'add-port-ids': [], 'del-port-ids': [], 'last-port': False } self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'update_firewall_group' ) as mock_driver_update_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'set_firewall_group_status' ) as mock_set_firewall_group_status: mock_driver_update_firewall_group.return_value = True self.api.update_firewall_group(self.context, firewall_group, host='host') calls = [ mock.call._get_firewall_group_ports( self.context, firewall_group, require_new_plugin=True, to_delete=True), mock.call._get_firewall_group_ports( self.context, firewall_group) ] mock_get_firewall_group_ports.assert_has_calls(calls) mock_get_in_ns_ports.assert_called mock_set_firewall_group_status.assert_called_once_with( self.context, firewall_group['id'], 'ACTIVE') def test_delete_firewall_group(self): firewall_group = {'id': 0, 'project_id': 1, 'admin_state_up': True, 'ports': [3, 4], 'add-port-ids': [], 'del-port-ids': [3, 4], 'last-port': False} self.api.plugin_rpc = mock.Mock() with mock.patch.object(self.api, '_get_firewall_group_ports' ) as mock_get_firewall_group_ports, \ mock.patch.object(self.api, '_get_in_ns_ports' ) as mock_get_in_ns_ports, \ mock.patch.object(self.api.fwaas_driver, 'delete_firewall_group' ) as mock_driver_delete_firewall_group, \ mock.patch.object(self.api.fwplugin_rpc, 'firewall_group_deleted' ) as mock_firewall_group_deleted: mock_driver_delete_firewall_group.return_value = True self.api.delete_firewall_group(self.context, firewall_group, host='host') mock_get_firewall_group_ports.assert_called_once_with( self.context, firewall_group, to_delete=True) mock_get_in_ns_ports.assert_called mock_firewall_group_deleted.assert_called_once_with(self.context, firewall_group['id']) def _prepare_router_data(self): return router_info.RouterInfo(self.api, self.router_id, **self.ri_kwargs) def test_get_in_ns_ports_for_non_ns_fw(self): port_ids = [1, 2] ports = [{'id': pid} for pid in port_ids] ri = self._prepare_router_data() ri.internal_ports = ports router_info = {ri.router_id: ri} api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None) self.api.consume_api(api_object) fw_port_ids = port_ids with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [] ports_for_fw_list = self.api._get_in_ns_ports(fw_port_ids) mock_list_netns.assert_called_with() self.assertFalse(ports_for_fw_list) def test_get_in_ns_ports_for_fw(self): port_ids = [1, 2] ports = [{'id': pid} for pid in port_ids] ri = self._prepare_router_data() ri.internal_ports = ports router_info = {} router_info[ri.router_id] = ri api_object = l3_agent_api.L3AgentExtensionAPI(router_info, None) self.api.consume_api(api_object) fw_port_ids = port_ids ports_for_fw_expected = [(ri, port_ids)] with mock.patch.object(ip_lib, 'list_network_namespaces') as mock_list_netns: mock_list_netns.return_value = [ri.ns_name] ports_for_fw_actual = self.api._get_in_ns_ports(fw_port_ids) self.assertEqual(ports_for_fw_expected, ports_for_fw_actual) def test_add_router_for_check_input(self): fw_agent = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') updated_router = { '_interfaces': [{ 'device_owner': 'network: router_interface', 'id': '1', 'tenant_id': 'demo_tenant_id', }], 'tenant_id': 'demo_tenant_id', 'id': '0b109a4e-d228-479d-ad43-08bf3245adbb', 'name': 'demo_router' } fwg = { 'status': 'ACTIVE', 'admin_state_up': True, 'tenant_id': 'demo_tenant_id', 'ports': [1], 'del-port-ids': [], 'add-port-ids': ['1'], 'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5' } with mock.patch('oslo_utils.importutils.import_object'): agent = fw_agent(cfg.CONF) agent.agent_api = mock.Mock() agent.fwplugin_rpc = mock.Mock() agent.conf.agent_mode = mock.Mock() agent.fwaas_driver = iptables_fwaas_v2.IptablesFwaasDriver() with mock.patch.object(agent.fwplugin_rpc, 'get_firewall_groups_for_project' ) as mock_get_firewall_groups_for_project, \ mock.patch.object(agent.agent_api, 'get_router_hosting_port' ) as mock_get_router_hosting_port, \ mock.patch.object(agent.fwaas_driver, '_get_ipt_mgrs_with_if_prefix' ) as mock_get_ipt_mgrs_with_if_prefix: mock_get_firewall_groups_for_project.return_value = [fwg] mock_get_router_hosting_port.return_value = mock.Mock() agent.add_router(self.context, updated_router) mock_get_ipt_mgrs_with_if_prefix.assert_any_call( agent.conf.agent_mode, mock.ANY) @mock.patch('oslo_utils.importutils.import_object') def test_add_router_with_several_ports(self, mock_import_object): fw_agent = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') updated_router = { '_interfaces': [ {'device_owner': 'network: router_interface', 'id': '1', 'tenant_id': 'demo_tenant_id'}, {'device_owner': 'network: router_interface', 'id': '2', 'tenant_id': 'demo_tenant_id'}, {'device_owner': 'network: router_interface', 'id': '3', 'tenant_id': 'demo_tenant_id'}], 'tenant_id': 'demo_tenant_id', 'id': '0b109a4e-d228-479d-ad43-08bf3245adbb', 'name': 'demo_router' } fwg1 = { 'status': 'ACTIVE', 'admin_state_up': True, 'tenant_id': 'demo_tenant_id', 'del-port-ids': [], 'add-port-ids': ['1', '3'], 'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5' } fwg2 = { 'status': 'ACTIVE', 'admin_state_up': True, 'tenant_id': 'demo_tenant_id', 'del-port-ids': [], 'add-port-ids': ['2', '3'], 'id': '2932b3d9-3a7b-48a1-a16c-bf9f7b2751a5' } agent = fw_agent(cfg.CONF) agent.agent_api = mock.Mock() agent.fwplugin_rpc = mock.Mock() agent.conf.agent_mode = mock.Mock() agent.fwaas_driver = iptables_fwaas_v2.IptablesFwaasDriver() patch_project = mock.patch.object( agent.fwplugin_rpc, 'get_firewall_groups_for_project') patch_invoke = mock.patch.object( agent, '_invoke_driver_for_sync_from_plugin') with patch_project as mock_get_firewall_groups, \ patch_invoke as mock_invoke_driver: mock_get_firewall_groups.return_value = [fwg1, fwg2] agent.add_router(self.context, updated_router) # Check that mock_invoke_driver was called exactly twice with # correct arguments. self.assertEqual([ mock.call(mock.ANY, {'1', '3'}, fwg1), mock.call(mock.ANY, {'2'}, fwg2), ], mock_invoke_driver.call_args_list) def test_add_router(self): fw_agent = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') new_router = { '_interfaces': [{ 'device_owner': 'network: router_interface', 'id': '1', 'tenant_id': 'demo_tenant_id', }], 'tenant_id': 'demo_tenant_id', 'id': '0b109a4e-d228-479d-ad43-08bf3245adbb', 'name': 'demo_router' } with mock.patch('oslo_utils.importutils.import_object'): agent = fw_agent(cfg.CONF) agent.agent_api = mock.Mock() agent.fwplugin_rpc = mock.Mock() agent.conf.agent_mode = mock.Mock() agent.fwaas_driver = iptables_fwaas_v2.IptablesFwaasDriver() with mock.patch.object(agent, '_process_router_update', ) as mock_process_router_update: agent.add_router(self.context, new_router) mock_process_router_update.assert_called_with(new_router) def test_update_router(self): fw_agent = _setup_test_agent_class([fwaas_constants.FIREWALL]) cfg.CONF.set_override('enabled', True, 'fwaas') updated_router = { '_interfaces': [{ 'device_owner': 'network: router_interface', 'id': '1', 'tenant_id': 'demo_tenant_id', }], 'tenant_id': 'demo_tenant_id', 'id': '0b109a4e-d228-479d-ad43-08bf3245adbb', 'name': 'demo_router' } with mock.patch('oslo_utils.importutils.import_object'): agent = fw_agent(cfg.CONF) agent.agent_api = mock.Mock() agent.fwplugin_rpc = mock.Mock() agent.conf.agent_mode = mock.Mock() agent.fwaas_driver = iptables_fwaas_v2.IptablesFwaasDriver() with mock.patch.object(agent, '_process_router_update', ) as mock_process_router_update: agent.update_router(self.context, updated_router) mock_process_router_update.assert_called_with(updated_router) ././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_agents.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_agents.p0000664000175000017500000007071300000000000034221 0ustar00zuulzuul00000000000000# Copyright 2016 # 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.conf import common as common_conf from neutron import extensions as neutron_extensions from neutron.tests.unit.extensions import test_l3 from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.db import api as db_api from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.plugins import directory from oslo_config import cfg from neutron_fwaas._i18n import _ from neutron_fwaas.db.firewall.v2.firewall_db_v2 import FirewallGroup from neutron_fwaas.services.firewall.service_drivers.agents import agents from neutron_fwaas.tests import base from neutron_fwaas.tests.unit.services.firewall import test_fwaas_plugin_v2 FIREWALL_AGENT_PLUGIN = ('neutron_fwaas.services.firewall.service_drivers.' 'agents.agents') FIREWALL_AGENT_PLUGIN_KLASS = FIREWALL_AGENT_PLUGIN + '.FirewallAgentDriver' DELETEFW_PATH = (FIREWALL_AGENT_PLUGIN + '.FirewallAgentApi.' 'delete_firewall_group') class FakeAgentApi(agents.FirewallAgentCallbacks): """This class used to mock the AgentAPI delete method It inherits from FirewallCallbacks because it needs access to the firewall_deleted method. The delete_firewall method belongs to the FirewallAgentApi, which has no access to the firewall_deleted method normally because it's not responsible for deleting the firewall from the DB. However, it needs to in the unit tests since there is no agent to call back. """ def __init__(self): return def delete_firewall_group(self, context, firewall_group, **kwargs): self.plugin = directory.get_plugin('FIREWALL_V2') self.firewall_db = self.plugin.driver.firewall_db self.firewall_group_deleted(context, firewall_group['id'], **kwargs) class TestFirewallAgentApi(base.BaseTestCase): def setUp(self): super(TestFirewallAgentApi, self).setUp() self.api = agents.FirewallAgentApi('topic', 'host') def test_init(self): self.assertEqual('topic', self.api.client.target.topic) self.assertEqual('host', self.api.host) def _call_test_helper(self, method_name): with mock.patch.object(self.api.client, 'cast') as rpc_mock, \ mock.patch.object(self.api.client, 'prepare') as prepare_mock: prepare_mock.return_value = self.api.client getattr(self.api, method_name)(mock.sentinel.context, 'test') prepare_args = {'fanout': True} prepare_mock.assert_called_once_with(**prepare_args) rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, firewall_group='test', host='host') def test_create_firewall_group(self): self._call_test_helper('create_firewall_group') def test_update_firewall_group(self): self._call_test_helper('update_firewall_group') def test_delete_firewall_group(self): self._call_test_helper('delete_firewall_group') class TestAgentDriver(test_fwaas_plugin_v2.FirewallPluginV2TestCase, test_l3.L3NatTestCaseMixin): def setUp(self): self._mock_agentapi_del_fw_p = mock.patch( DELETEFW_PATH, create=True, new=FakeAgentApi().delete_firewall_group, ) self.agentapi_del_fw_p = self._mock_agentapi_del_fw_p.start() self.addCleanup(self._mock_agentapi_del_fw_p.stop) self._mock_get_client = mock.patch.object(agents.n_rpc, 'get_client') self._mock_get_client.start() self.addCleanup(self._mock_get_client.stop) mock.patch.object(agents.n_rpc, 'Connection').start() l3_plugin_str = ('neutron.tests.unit.extensions.test_l3.' 'TestL3NatServicePlugin') l3_plugin = {'l3_plugin_name': l3_plugin_str} common_conf.register_core_common_config_opts(cfg=cfg.CONF) super(TestAgentDriver, self).setUp( service_provider=FIREWALL_AGENT_PLUGIN_KLASS, extra_service_plugins=l3_plugin, extra_extension_paths=neutron_extensions.__path__) self.db = self.plugin.driver.firewall_db self.callbacks = agents.FirewallAgentCallbacks(self.db) router_distributed_opts = [ cfg.BoolOpt( 'router_distributed', default=False, help=_("System-wide flag to determine the type of router " "that tenants can create. Only admin can override.")), ] cfg.CONF.register_opts(router_distributed_opts) @property def _self_context(self): return context.Context('', self._tenant_id) def _get_test_firewall_group_attrs(self, name, status=nl_constants.INACTIVE): return super(TestAgentDriver, self)._get_test_firewall_group_attrs( name, status=status) def test_set_firewall_group_status(self): ctx = context.get_admin_context() with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP ) as fwg: fwg_id = fwg['firewall_group']['id'] res = self.callbacks.set_firewall_group_status(ctx, fwg_id, nl_constants.ACTIVE) fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) self.assertEqual(nl_constants.ACTIVE, fwg_db['status']) self.assertTrue(res) res = self.callbacks.set_firewall_group_status(ctx, fwg_id, nl_constants.ERROR) fwg_db = self.plugin.get_firewall_group(ctx, fwg_id) self.assertEqual(nl_constants.ERROR, fwg_db['status']) self.assertFalse(res) def test_firewall_group_deleted(self): ctx = context.get_admin_context() with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP, do_delete=False ) as fwg: fwg_id = fwg['firewall_group']['id'] with db_api.CONTEXT_WRITER.using(ctx): fwg_db = self.db._get_firewall_group(ctx, fwg_id) fwg_db['status'] = nl_constants.PENDING_DELETE observed = self.callbacks.firewall_group_deleted(ctx, fwg_id) self.assertTrue(observed) self.assertRaises(f_exc.FirewallGroupNotFound, self.plugin.get_firewall_group, ctx, fwg_id) def test_firewall_group_deleted_concurrently(self): ctx = context.get_admin_context() alt_ctx = context.get_admin_context() _get_firewall_group = self.db._get_firewall_group def getdelete(context, fwg_id): fwg_db = _get_firewall_group(context, fwg_id) # NOTE(cby): Use a different session to simulate a concurrent del with db_api.CONTEXT_READER.using(alt_ctx): alt_ctx.session.query(FirewallGroup).filter_by( id=fwg_id).delete() return fwg_db with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP, do_delete=False, as_admin=True, ) as fwg: fwg_id = fwg['firewall_group']['id'] with db_api.CONTEXT_WRITER.using(ctx): fwg_db = self.db._get_firewall_group(ctx, fwg_id) fwg_db['status'] = nl_constants.PENDING_DELETE ctx.session.flush() with mock.patch.object( self.db, '_get_firewall_group', side_effect=getdelete ): observed = self.callbacks.firewall_group_deleted( ctx, fwg_id) self.assertTrue(observed) self.assertRaises(f_exc.FirewallGroupNotFound, self.plugin.get_firewall_group, ctx, fwg_id) def test_firewall_group_deleted_not_found(self): ctx = context.get_admin_context() observed = self.callbacks.firewall_group_deleted( ctx, 'notfound') self.assertTrue(observed) def test_firewall_group_deleted_error(self): ctx = context.get_admin_context() with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP, as_admin=True, ) as fwg: fwg_id = fwg['firewall_group']['id'] observed = self.callbacks.firewall_group_deleted( ctx, fwg_id) self.assertFalse(observed) fwg_db = self.db._get_firewall_group(ctx, fwg_id) self.assertEqual(nl_constants.ERROR, fwg_db['status']) def test_create_firewall_group_ports_not_specified(self): """neutron firewall-create test-policy """ with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) def test_create_firewall_group_with_ports(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) def test_create_firewall_group_with_ports_on_diff_routers(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r2, \ self.subnet() as s3: body = self._router_interface_action( 'add', r2['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] fwg_ports = [port_id1, port_id2, port_id3] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) def test_create_firewall_group_with_ports_no_policy(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) def test_update_firewall_group_with_new_ports_no_policy(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2, \ self.subnet(cidr='30.0.0.0/24') as s3: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) data = {'firewall_group': {'ports': [port_id2, port_id3]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(sorted([port_id2, port_id3]), sorted(res['firewall_group']['ports'])) self.assertEqual(nl_constants.INACTIVE, res['firewall_group']['status']) def test_update_firewall_group_with_new_ports_status_pending(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2, \ self.subnet(cidr='30.0.0.0/24') as s3: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] body = self._router_interface_action( 'add', r['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) data = {'firewall_group': {'ports': [port_id2, port_id3]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id']) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_firewall_group_with_new_ports_status_active(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2, \ self.subnet(cidr='30.0.0.0/24') as s3: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] body = self._router_interface_action( 'add', r['router']['id'], s3['subnet']['id'], None) port_id3 = body['port_id'] with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) ctx = context.get_admin_context() self.callbacks.set_firewall_group_status(ctx, fwg1['firewall_group']['id'], nl_constants.ACTIVE) data = {'firewall_group': {'ports': [port_id2, port_id3]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(sorted([port_id2, port_id3]), sorted(res['firewall_group']['ports'])) def test_update_firewall_rule_on_active_fwg(self): name = "new_firewall_rule1" attrs = self._get_test_firewall_rule_attrs(name) with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] with self.firewall_rule(as_admin=True) as fwr: with self.firewall_policy( firewall_rules=[fwr['firewall_rule']['id']], as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=[port_id1], admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) ctx = context.get_admin_context() self.callbacks.set_firewall_group_status(ctx, fwg1['firewall_group']['id'], nl_constants.ACTIVE) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) for k, v in attrs.items(): self.assertEqual(v, res['firewall_rule'][k]) def test_update_firewall_rule_on_pending_create_fwg(self): """update should fail""" name = "new_firewall_rule1" with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] with self.firewall_rule(as_admin=True) as fwr: with self.firewall_policy( firewall_rules=[fwr['firewall_rule']['id']], as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( name='test', ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, ports=[port_id1], admin_state_up=True) as fwg1: self.assertEqual(nl_constants.PENDING_CREATE, fwg1['firewall_group']['status']) data = {'firewall_rule': {'name': name}} req = self.new_update_request('firewall_rules', data, fwr['firewall_rule']['id']) res = req.get_response(self.ext_api) self.assertEqual(409, res.status_int) def test_update_firewall_group_with_non_exist_ports(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet(cidr='30.0.0.0/24') as s: body = self._router_interface_action( 'add', r['router']['id'], s['subnet']['id'], None) port_id1 = body['port_id'] foo_port_id = 'caef152d-b118-4b9b-bc77-800661bf082d' fwg_ports = [port_id1] with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) data = {'firewall_group': {'ports': [foo_port_id]}} req = self.new_update_request('firewall_groups', data, fwg1['firewall_group']['id']) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual('PortNotFound', res['NeutronError']['type']) def test_update_firewall_group_with_ports_and_policy(self): """neutron firewall_group create test-policy """ with self.router(name='router1', admin_state_up=True, tenant_id=self._tenant_id) as r, \ self.subnet() as s1, \ self.subnet(cidr='20.0.0.0/24') as s2: body = self._router_interface_action( 'add', r['router']['id'], s1['subnet']['id'], None) port_id1 = body['port_id'] body = self._router_interface_action( 'add', r['router']['id'], s2['subnet']['id'], None) port_id2 = body['port_id'] fwg_ports = [port_id1, port_id2] with self.firewall_rule(as_admin=True) as fwr: with self.firewall_policy( firewall_rules=[fwr['firewall_rule']['id']], as_admin=True) as fwp: with self.firewall_group( name='test', default_policy=False, ports=fwg_ports, admin_state_up=True) as fwg1: self.assertEqual(nl_constants.INACTIVE, fwg1['firewall_group']['status']) fwp_id = fwp["firewall_policy"]["id"] data = {'firewall_group': {'ports': fwg_ports}} req = (self. new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context)) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(nl_constants.INACTIVE, res['firewall_group']['status']) data = {'firewall_group': { 'ingress_firewall_policy_id': fwp_id}} req = (self. new_update_request('firewall_groups', data, fwg1['firewall_group']['id'], context=self._self_context)) res = self.deserialize(self.fmt, req.get_response(self.ext_api)) self.assertEqual(nl_constants.PENDING_UPDATE, res['firewall_group']['status']) def test_create_firewall_group_with_dvr(self): cfg.CONF.set_override('router_distributed', True) attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) def test_create_firewall_group(self): attrs = self._get_test_firewall_group_attrs("firewall1") self._test_create_firewall_group(attrs) def test_create_firewall_group_with_empty_ports(self): attrs = self._get_test_firewall_group_attrs("fwg1") attrs['ports'] = [] self._test_create_firewall_group(attrs) ././@PaxHeader0000000000000000000000000000022100000000000011450 xustar0000000000000000123 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_agent_api.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall0000664000175000017500000000622100000000000034300 0ustar00zuulzuul00000000000000# Copyright (c) 2013 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. from unittest import mock from neutron_fwaas.services.firewall.service_drivers.agents.drivers \ import fwaas_base from neutron_fwaas.services.firewall.service_drivers.agents.drivers \ import fwaas_base_v2 from neutron_fwaas.services.firewall.service_drivers.agents \ import firewall_agent_api as api from neutron_fwaas.tests import base class NoopFwaasDriver(fwaas_base.FwaasDriverBase): """Noop Fwaas Driver. v1 firewall driver which does nothing. This driver is for disabling Fwaas functionality. """ def create_firewall_group(self, agent_mode, apply_list, firewall): pass def delete_firewall_group(self, agent_mode, apply_list, firewall): pass def update_firewall_group(self, agent_mode, apply_list, firewall): pass def apply_default_policy(self, agent_mode, apply_list, firewall): pass class NoopFwaasDriverV2(fwaas_base_v2.FwaasDriverBase): """Noop Fwaas Driver. v2 firewall driver which does nothing. This driver is for disabling Fwaas functionality. """ def create_firewall_group(self, agent_mode, apply_list, firewall): pass def delete_firewall_group(self, agent_mode, apply_list, firewall): pass def update_firewall_group(self, agent_mode, apply_list, firewall): pass def apply_default_policy(self, agent_mode, apply_list, firewall): pass class TestFWaaSAgentApi(base.BaseTestCase): def setUp(self): super(TestFWaaSAgentApi, self).setUp() self.api = api.FWaaSPluginApiMixin( 'topic', 'host') def test_init(self): self.assertEqual('host', self.api.host) def _test_firewall_method(self, method_name, **kwargs): with mock.patch.object(self.api.client, 'call') as rpc_mock, \ mock.patch.object(self.api.client, 'prepare') as prepare_mock: prepare_mock.return_value = self.api.client getattr(self.api, method_name)(mock.sentinel.context, 'test', **kwargs) prepare_args = {} prepare_mock.assert_called_once_with(**prepare_args) rpc_mock.assert_called_once_with(mock.sentinel.context, method_name, firewall_id='test', host='host', **kwargs) def test_set_firewall_status(self): self._test_firewall_method('set_firewall_status', status='fake_status') def test_firewall_deleted(self): self._test_firewall_method('firewall_deleted') ././@PaxHeader0000000000000000000000000000021700000000000011455 xustar0000000000000000121 path=neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_service.py 22 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall0000664000175000017500000000465200000000000034306 0ustar00zuulzuul00000000000000# Copyright 2014 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. from neutron.tests import base from oslo_config import cfg from neutron_fwaas.services.firewall.service_drivers.agents import\ firewall_service FWAAS_NOP_DEVICE = ('neutron_fwaas.tests.unit.services.firewall.' 'service_drivers.agents.test_firewall_agent_api.' 'NoopFwaasDriver') class TestFirewallDeviceDriverLoading(base.BaseTestCase): def setUp(self): super(TestFirewallDeviceDriverLoading, self).setUp() self.service = firewall_service.FirewallService() def test_loading_firewall_device_driver(self): """Get the sole device driver for FWaaS.""" cfg.CONF.set_override('driver', FWAAS_NOP_DEVICE, 'fwaas') driver = self.service.load_device_drivers() self.assertIsNotNone(driver) self.assertIn(driver.__class__.__name__, FWAAS_NOP_DEVICE) def test_fail_no_such_firewall_device_driver(self): """Failure test of import error for FWaaS device driver.""" cfg.CONF.set_override('driver', 'no.such.class', 'fwaas') self.assertRaises(ImportError, self.service.load_device_drivers) def test_fail_firewall_no_device_driver_specified(self): """Failure test when no FWaaS device driver is specified. This is a configuration error, as the user must specify a device driver, when enabling the firewall service (and there is no default configuration set. We'll simulate that by using an empty string. """ cfg.CONF.set_override('driver', '', 'fwaas') self.assertRaises(ValueError, self.service.load_device_drivers) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/service_drivers/test_driver_api.py0000664000175000017500000003157200000000000033774 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import copy from unittest import mock from neutron_lib.callbacks import events from neutron_lib import context from neutron_fwaas.common import fwaas_constants as const from neutron_fwaas.tests.unit.services.firewall import test_fwaas_plugin_v2 class FireWallDriverDBMixinTestCase(test_fwaas_plugin_v2. FirewallPluginV2TestCase): def setUp(self): provider = ('neutron_fwaas.services.firewall.service_drivers.' 'driver_api.FirewallDriverDB') super(FireWallDriverDBMixinTestCase, self).setUp( service_provider=provider) self._mp_registry_publish = mock.patch( 'neutron_lib.callbacks.registry.publish') self.mock_registry_publish = self._mp_registry_publish.start() self.addCleanup(self._mp_registry_publish.stop) self.driver_api = self.plugin.driver self.ctx = context.get_admin_context() self.firewall_db = self.plugin.driver.firewall_db self.m_payload = mock.Mock() self._mock_payload = mock.patch( 'neutron_lib.callbacks.events.DBEventPayload') m_db_event_payload = self._mock_payload.start() self.addCleanup(self._mock_payload.stop) m_db_event_payload.return_value = self.m_payload self.fake_fwg = { 'id': 'fake_fwg_id', 'ingress_firewall_policy_id': 'fake_ifwp_id', 'egress_firewall_policy_id': 'fake_efwp_id', 'ports': [], 'tenant_id': 'fake_tenant_id', 'status': 'CREATED' } self.fake_fwp = { 'id': 'fake_fwp_id', 'firewall_rules': [], 'info': 'fake_rule_info', 'project_id': 'fake_project_id' } self.fake_fwr = { 'id': 'fake_fwr_id', 'firewall_policy_id': [], 'project_id': 'fake_project_id' } # Test Firewall Group def test_create_firewall_group(self): with mock.patch.object(self.firewall_db, 'create_firewall_group', return_value=self.fake_fwg): self.driver_api.create_firewall_group_postcommit = mock.Mock() self.driver_api.create_firewall_group(self.ctx, self.fake_fwg) self.driver_api.create_firewall_group_postcommit.\ assert_called_once_with(self.ctx, self.fake_fwg) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_GROUP, events.AFTER_CREATE, self.driver_api, payload=self.m_payload) def test_delete_firewall_group(self): with mock.patch.object(self.firewall_db, 'get_firewall_group', return_value=self.fake_fwg): self.driver_api.delete_firewall_group_postcommit = mock.Mock() self.driver_api.delete_firewall_group(self.ctx, 'fake_fwg_id') self.driver_api.delete_firewall_group_postcommit.\ assert_called_once_with(self.ctx, self.fake_fwg) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_GROUP, events.AFTER_DELETE, self.driver_api, payload=self.m_payload) def test_update_firewall_group(self): fake_fwg_delta = { 'ingress_firewall_policy_id': 'fake_ifwp_delta_id', 'egress_firewall_policy_id': 'fake_efwp_delta_id', 'ports': [], } old_fake_fwg = { 'id': 'fake_fwg_id', 'ingress_firewall_policy_id': 'old_fake_ifwp_id', 'egress_firewall_policy_id': 'old_fake_efwp_id', 'ports': [], 'tenant_id': 'fake_tenant_id', 'status': 'CREATED' } with mock.patch.object(self.firewall_db, 'get_firewall_group', return_value=old_fake_fwg): new_fake_fwg = copy.deepcopy(old_fake_fwg) new_fake_fwg.update(fake_fwg_delta) with mock.patch.object(self.firewall_db, 'update_firewall_group', return_value=new_fake_fwg): self.driver_api.\ update_firewall_group_postcommit = mock.Mock() self.driver_api.\ update_firewall_group(self.ctx, 'fake_fwg_id', fake_fwg_delta) self.driver_api.update_firewall_group_postcommit.\ assert_called_once_with(self.ctx, old_fake_fwg, new_fake_fwg) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_GROUP, events.AFTER_UPDATE, self.driver_api, payload=self.m_payload) # Test Firewall Policy def test_create_firewall_policy(self): with mock.patch.object(self.firewall_db, 'create_firewall_policy', return_value=self.fake_fwp): self.driver_api.create_firewall_policy_postcommit = mock.Mock() self.driver_api.create_firewall_policy(self.ctx, self.fake_fwp) self.driver_api.create_firewall_policy_postcommit.\ assert_called_once_with(self.ctx, self.fake_fwp) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_POLICY, events.AFTER_CREATE, self.driver_api, payload=self.m_payload) def test_delete_firewall_policy(self): with mock.patch.object(self.firewall_db, 'delete_firewall_policy'): with mock.patch.object(self.firewall_db, 'get_firewall_policy', return_value=self.fake_fwp): self.driver_api.\ delete_firewall_policy_postcommit = mock.Mock() self.driver_api.\ delete_firewall_policy(self.ctx, 'fake_fwp_id') self.driver_api.delete_firewall_policy_postcommit.\ assert_called_once_with(self.ctx, self.fake_fwp) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_POLICY, events.AFTER_UPDATE, self.driver_api, payload=self.m_payload) def test_update_firewall_policy(self): fake_fwp_delta = { 'firewall_rules': [], } old_fake_fwp = { 'id': 'fake_fwp_id', 'firewall_rules': [], 'project_id': 'fake_project_id' } with mock.patch.object(self.firewall_db, 'get_firewall_policy', return_value=old_fake_fwp): new_fake_fwp = copy.deepcopy(old_fake_fwp) new_fake_fwp.update(fake_fwp_delta) with mock.patch.object(self.firewall_db, 'update_firewall_policy', return_value=new_fake_fwp): self.driver_api.\ update_firewall_policy_postcommit = mock.Mock() self.driver_api.\ update_firewall_policy(self.ctx, 'fake_fwp_id', fake_fwp_delta) self.driver_api.update_firewall_policy_postcommit.\ assert_called_once_with(self.ctx, old_fake_fwp, new_fake_fwp) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_POLICY, events.AFTER_UPDATE, self.driver_api, payload=self.m_payload) # Test Firewall Rule def test_create_firewall_rule(self): with mock.patch.object(self.firewall_db, 'create_firewall_rule', return_value=self.fake_fwr): self.driver_api.create_firewall_rule_postcommit = mock.Mock() self.driver_api.create_firewall_rule(self.ctx, self.fake_fwr) self.driver_api.create_firewall_rule_postcommit.\ assert_called_once_with(self.ctx, self.fake_fwr) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_RULE, events.AFTER_CREATE, self.driver_api, payload=self.m_payload) def test_delete_firewall_rule(self): self.firewall_db.delete_firewall_rule = mock.Mock() with mock.patch.object(self.firewall_db, 'get_firewall_rule', return_value=self.fake_fwr): self.driver_api.\ delete_firewall_rule_postcommit = mock.Mock() self.driver_api.\ delete_firewall_rule(self.ctx, 'fake_fwr_id') self.driver_api.delete_firewall_rule_postcommit.\ assert_called_once_with(self.ctx, self.fake_fwr) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_RULE, events.AFTER_DELETE, self.driver_api, payload=self.m_payload) def test_update_firewall_rule(self): fake_fwr_delta = { 'firewall_policy_id': [], } old_fake_fwr = { 'id': 'fake_fwr_id', 'firewall_policy_id': [], 'project_id': 'fake_project_id' } with mock.patch.object(self.firewall_db, 'get_firewall_rule', return_value=old_fake_fwr): new_fake_fwr = copy.deepcopy(old_fake_fwr) new_fake_fwr.update(fake_fwr_delta) with mock.patch.object(self.firewall_db, 'update_firewall_rule', return_value=new_fake_fwr): self.driver_api.\ update_firewall_rule_postcommit = mock.Mock() self.driver_api. \ update_firewall_rule(self.ctx, 'fake_fwr_id', fake_fwr_delta) self.driver_api.update_firewall_rule_postcommit.\ assert_called_once_with(self.ctx, old_fake_fwr, new_fake_fwr) self.mock_registry_publish.\ assert_called_with(const.FIREWALL_RULE, events.AFTER_UPDATE, self.driver_api, payload=self.m_payload) def test_insert_rule(self): with mock.patch.object(self.firewall_db, 'insert_rule', return_value=self.fake_fwp): self.driver_api.insert_rule_postcommit = mock.Mock() self.driver_api.insert_rule(self.ctx, 'fake_fwp_id', 'fake_rule_info') self.driver_api.insert_rule_postcommit.\ assert_called_once_with(self.ctx, 'fake_fwp_id', 'fake_rule_info') self.mock_registry_publish.\ assert_called_with(const.FIREWALL_POLICY, events.AFTER_UPDATE, self.driver_api, payload=self.m_payload) def test_remove_rule(self): with mock.patch.object(self.firewall_db, 'remove_rule', return_value=self.fake_fwp): self.driver_api.remove_rule_postcommit = mock.Mock() self.driver_api.remove_rule(self.ctx, 'fake_fwp_id', 'fake_rule_info') self.driver_api.remove_rule_postcommit.\ assert_called_once_with(self.ctx, 'fake_fwp_id', 'fake_rule_info') self.mock_registry_publish.\ assert_called_with(const.FIREWALL_POLICY, events.AFTER_UPDATE, self.driver_api, payload=self.m_payload) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py0000664000175000017500000010263000000000000031532 0ustar00zuulzuul00000000000000# Copyright 2016 # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import contextlib from unittest import mock import webob.exc from neutron.api import extensions as api_ext from neutron.db import servicetype_db as sdb from neutron.tests.unit.db import test_db_base_plugin_v2 as test_db_plugin from neutron_lib.api.definitions import firewall_v2 from neutron_lib.callbacks import events from neutron_lib import constants as nl_constants from neutron_lib import context from neutron_lib.exceptions import firewall_v2 as f_exc from neutron_lib.plugins import directory from oslo_utils import importutils from neutron_fwaas.common import fwaas_constants from neutron_fwaas import extensions from neutron_fwaas.services.firewall import fwaas_plugin_v2 from neutron_fwaas.services.firewall.service_drivers.driver_api import \ FirewallDriverDB def http_client_error(req, res): explanation = "Request '%s %s %s' failed: %s" % (req.method, req.url, req.body, res.body) return webob.exc.HTTPClientError(code=res.status_int, explanation=explanation) class DummyDriverDB(FirewallDriverDB): def is_supported_l2_port(self, port): return True def is_supported_l3_port(self, port): return True class FirewallPluginV2TestCase(test_db_plugin.NeutronDbPluginV2TestCase): DESCRIPTION = 'default description' PROTOCOL = 'tcp' IP_VERSION = 4 SOURCE_IP_ADDRESS_RAW = '1.1.1.1' DESTINATION_IP_ADDRESS_RAW = '2.2.2.2' SOURCE_PORT = '55000:56000' DESTINATION_PORT = '56000:57000' ACTION = 'allow' AUDITED = True ENABLED = True ADMIN_STATE_UP = True SHARED = True resource_prefix_map = dict( (k, firewall_v2.API_PREFIX) for k in firewall_v2.RESOURCE_ATTRIBUTE_MAP.keys() ) def setUp(self, service_provider=None, core_plugin=None, extra_service_plugins=None, extra_extension_paths=None): provider = fwaas_constants.FIREWALL_V2 if not service_provider: provider += (':dummy:neutron_fwaas.tests.unit.services.firewall.' 'test_fwaas_plugin_v2.DummyDriverDB:default') else: provider += ':test:' + service_provider + ':default' bits = provider.split(':') provider = { 'service_type': bits[0], 'name': bits[1], 'driver': bits[2], 'default': True, } # override the default service provider self.service_providers = ( mock.patch.object(sdb.ServiceTypeManager, 'get_service_providers').start()) self.service_providers.return_value = [provider] plugin_str = ('neutron_fwaas.services.firewall.fwaas_plugin_v2.' 'FirewallPluginV2') service_plugins = {'fw_plugin_name': plugin_str} service_plugins.update(extra_service_plugins or {}) # we need to provide a plugin instance, although the extension manager # will create a new instance of the plugin plugins = { fwaas_constants.FIREWALL_V2: fwaas_plugin_v2.FirewallPluginV2(), } for plugin_name, plugin_str in (extra_service_plugins or {}).items(): plugins[plugin_name] = importutils.import_object(plugin_str) ext_mgr = api_ext.PluginAwareExtensionManager( ':'.join(extensions.__path__ + (extra_extension_paths or [])), plugins, ) super(FirewallPluginV2TestCase, self).setUp( plugin=core_plugin, service_plugins=service_plugins, ext_mgr=ext_mgr, ) # find the Firewall plugin that was instantiated by the extension # manager self.plugin = directory.get_plugin(fwaas_constants.FIREWALL_V2) def _get_admin_context(self): # FIXME NOTE(ivasilevskaya) seems that test framework treats context # with user_id=None/tenant_id=None (return value of # context._get_admin_context() method) in a somewhat special way. # So as a workaround to have the framework behave properly right now # let's implement our own _get_admin_context method and look into the # matter some other time. return context.Context(user_id='admin', tenant_id='admin-tenant', is_admin=True).elevated() def _get_nonadmin_context(self, user_id='non-admin', tenant_id='tenant1'): return context.Context(user_id=user_id, tenant_id=tenant_id) def _test_list_resources(self, resource, items, neutron_context=None, query_params=None, as_admin=False): if resource.endswith('y'): resource_plural = resource.replace('y', 'ies') else: resource_plural = resource + 's' res = self._list(resource_plural, query_params=query_params, as_admin=as_admin) resource = resource.replace('-', '_') self.assertEqual( sorted([i[resource]['id'] for i in items]), sorted([i['id'] for i in res[resource_plural]])) def _list_req(self, resource_plural, ctx=None, as_admin=False): if not ctx: ctx = self._get_admin_context() req = self.new_list_request(resource_plural, as_admin=as_admin) req.environ['neutron.context'] = ctx return self.deserialize( self.fmt, req.get_response(self.ext_api))[resource_plural] def _show_req(self, resource_plural, obj_id, ctx=None): req = self.new_show_request(resource_plural, obj_id, fmt=self.fmt) if not ctx: ctx = self._get_admin_context() req.environ['neutron.context'] = ctx res = self.deserialize( self.fmt, req.get_response(self.ext_api)) return res def _build_default_fwg(self, ctx=None, is_one=True, as_admin=False): res = self._list_req('firewall_groups', ctx=ctx, as_admin=as_admin) if is_one: self.assertEqual(1, len(res)) return res[0] return res def _get_test_firewall_rule_attrs(self, name='firewall_rule1'): attrs = {'name': name, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'protocol': self.PROTOCOL, 'ip_version': self.IP_VERSION, 'source_ip_address': self.SOURCE_IP_ADDRESS_RAW, 'destination_ip_address': self.DESTINATION_IP_ADDRESS_RAW, 'source_port': self.SOURCE_PORT, 'destination_port': self.DESTINATION_PORT, 'action': self.ACTION, 'enabled': self.ENABLED, 'shared': self.SHARED} return attrs def _get_test_firewall_policy_attrs(self, name='firewall_policy1', audited=AUDITED): attrs = {'name': name, 'description': self.DESCRIPTION, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'firewall_rules': [], 'audited': audited, 'shared': self.SHARED} return attrs def _get_test_firewall_group_attrs(self, name='firewall_1', status=nl_constants.CREATED): attrs = {'name': name, 'tenant_id': self._tenant_id, 'project_id': self._tenant_id, 'admin_state_up': self.ADMIN_STATE_UP, 'status': status} return attrs def _create_firewall_policy(self, fmt, name, description, shared, firewall_rules, audited, expected_res_status=None, as_admin=False, **kwargs): data = {'firewall_policy': {'name': name, 'description': description, 'firewall_rules': firewall_rules, 'audited': audited, 'shared': shared}} ctx = kwargs.get('context', None) if ctx is None or ctx.is_admin: tenant_id = kwargs.get('tenant_id', self._tenant_id) data['firewall_policy'].update({'tenant_id': tenant_id}) data['firewall_policy'].update({'project_id': tenant_id}) req = self.new_create_request('firewall_policies', data, fmt, context=ctx, as_admin=as_admin) res = req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, res.status_int) elif res.status_int >= 400: raise http_client_error(req, res) return res def _replace_firewall_status(self, attrs, old_status, new_status): if attrs['status'] is old_status: attrs['status'] = new_status return attrs @contextlib.contextmanager def firewall_policy(self, fmt=None, name='firewall_policy1', description=DESCRIPTION, shared=SHARED, firewall_rules=None, audited=True, do_delete=True, as_admin=False, **kwargs): if firewall_rules is None: firewall_rules = [] if not fmt: fmt = self.fmt res = self._create_firewall_policy(fmt, name, description, shared, firewall_rules, audited, as_admin=as_admin, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_policy = self.deserialize(fmt or self.fmt, res) yield firewall_policy if do_delete: self._delete('firewall_policies', firewall_policy['firewall_policy']['id'], as_admin=True) def _create_firewall_rule(self, fmt, name, shared, protocol, ip_version, source_ip_address, destination_ip_address, source_port, destination_port, action, enabled, expected_res_status=None, as_admin=False, **kwargs): tenant_id = kwargs.get('tenant_id', self._tenant_id) data = {'firewall_rule': {'name': name, 'protocol': protocol, 'ip_version': ip_version, 'source_ip_address': source_ip_address, 'destination_ip_address': destination_ip_address, 'source_port': source_port, 'destination_port': destination_port, 'action': action, 'enabled': enabled, 'shared': shared}} ctx = kwargs.get('context', None) if ctx is None or ctx.is_admin: tenant_id = kwargs.get('tenant_id', self._tenant_id) data['firewall_rule'].update({'tenant_id': tenant_id}) data['firewall_rule'].update({'project_id': tenant_id}) req = self.new_create_request('firewall_rules', data, fmt, context=ctx, as_admin=as_admin) res = req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, res.status_int) elif res.status_int >= 400: raise http_client_error(req, res) return res @contextlib.contextmanager def firewall_rule(self, fmt=None, name='firewall_rule1', shared=SHARED, protocol=PROTOCOL, ip_version=IP_VERSION, source_ip_address=SOURCE_IP_ADDRESS_RAW, destination_ip_address=DESTINATION_IP_ADDRESS_RAW, source_port=SOURCE_PORT, destination_port=DESTINATION_PORT, action=ACTION, enabled=ENABLED, do_delete=True, as_admin=False, **kwargs): if not fmt: fmt = self.fmt res = self._create_firewall_rule(fmt, name, shared, protocol, ip_version, source_ip_address, destination_ip_address, source_port, destination_port, action, enabled, as_admin=as_admin, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_rule = self.deserialize(fmt or self.fmt, res) yield firewall_rule if do_delete: self._delete('firewall_rules', firewall_rule['firewall_rule']['id'], as_admin=True) def _create_firewall_group(self, fmt, name, description, ingress_firewall_policy_id=None, egress_firewall_policy_id=None, ports=None, admin_state_up=True, expected_res_status=None, as_admin=False, **kwargs): if ingress_firewall_policy_id is None: default_policy = kwargs.get('default_policy', True) if default_policy: res = self._create_firewall_policy( fmt, 'fwp', description=self.DESCRIPTION, shared=self.SHARED, firewall_rules=[], audited=self.AUDITED, as_admin=as_admin, ) firewall_policy = self.deserialize(fmt or self.fmt, res) fwp_id = firewall_policy["firewall_policy"]["id"] ingress_firewall_policy_id = fwp_id data = {'firewall_group': {'name': name, 'description': description, 'ingress_firewall_policy_id': ingress_firewall_policy_id, 'egress_firewall_policy_id': egress_firewall_policy_id, 'admin_state_up': admin_state_up}} ctx = kwargs.get('context', None) if ctx is None or ctx.is_admin: tenant_id = kwargs.get('tenant_id', self._tenant_id) data['firewall_group'].update({'tenant_id': tenant_id}) data['firewall_group'].update({'project_id': tenant_id}) if ports is not None: data['firewall_group'].update({'ports': ports}) req = self.new_create_request('firewall_groups', data, fmt, context=ctx, as_admin=as_admin) res = req.get_response(self.ext_api) if expected_res_status: self.assertEqual(expected_res_status, res.status_int) elif res.status_int >= 400: raise http_client_error(req, res) return res @contextlib.contextmanager def firewall_group(self, fmt=None, name='firewall_1', description=DESCRIPTION, ingress_firewall_policy_id=None, egress_firewall_policy_id=None, ports=None, admin_state_up=True, do_delete=True, as_admin=False, **kwargs): if not fmt: fmt = self.fmt res = self._create_firewall_group(fmt, name, description, ingress_firewall_policy_id, egress_firewall_policy_id, ports=ports, admin_state_up=admin_state_up, as_admin=as_admin, **kwargs) if res.status_int >= 400: raise webob.exc.HTTPClientError(code=res.status_int) firewall_group = self.deserialize(fmt or self.fmt, res) yield firewall_group if do_delete: self.plugin.driver.firewall_db.update_firewall_group_status( context.get_admin_context(), firewall_group['firewall_group']['id'], nl_constants.ACTIVE) data = { 'firewall_group': { 'ports': [], }, } req = self.new_update_request( 'firewall_groups', data, firewall_group['firewall_group']['id'], as_admin=True, ) req.get_response(self.ext_api) self._delete('firewall_groups', firewall_group['firewall_group']['id'], as_admin=True) def _rule_action(self, action, id, firewall_rule_id, insert_before=None, insert_after=None, expected_code=webob.exc.HTTPOk.code, expected_body=None, body_data=None, as_admin=False): # We intentionally do this check for None since we want to distinguish # from empty dictionary if body_data is None: if action == 'insert': body_data = {'firewall_rule_id': firewall_rule_id, 'insert_before': insert_before, 'insert_after': insert_after} else: body_data = {'firewall_rule_id': firewall_rule_id} req = self.new_action_request('firewall_policies', body_data, id, "%s_rule" % action, as_admin=as_admin) res = req.get_response(self.ext_api) self.assertEqual(expected_code, res.status_int) response = self.deserialize(self.fmt, res) if 'standard_attr_id' in response: del response['standard_attr_id'] if expected_body: self.assertEqual(expected_body, response) return response def _compare_firewall_rule_lists(self, firewall_policy_id, observed_list, expected_list): position = 0 for r1, r2 in zip(observed_list, expected_list): rule = r1['firewall_rule'] rule['firewall_policy_id'] = firewall_policy_id position += 1 rule['position'] = position for k in rule: self.assertEqual(r2[k], rule[k]) def _test_create_firewall_group(self, attrs): with self.firewall_policy(as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] attrs['ingress_firewall_policy_id'] = fwp_id attrs['egress_firewall_policy_id'] = fwp_id with self.firewall_group( name=attrs['name'], ingress_firewall_policy_id=fwp_id, egress_firewall_policy_id=fwp_id, admin_state_up=self.ADMIN_STATE_UP, ports=attrs['ports'] if 'ports' in attrs else None, ) as firewall_group: for k, v in attrs.items(): self.assertEqual(v, firewall_group['firewall_group'][k]) class TestFirewallPluginBasev2(FirewallPluginV2TestCase): def _test_fwg_with_port(self, device_owner): with self.port(device_owner=device_owner) as port: with self.firewall_rule(as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(firewall_rules=[fwr_id], as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] self.firewall_group( self.fmt, "firewall_group", self.DESCRIPTION, ports=[port['port']['id']], ingress_firewall_policy_id=fwp_id, ) def test_create_fwg_with_l3_ports(self): for device_owner_for_l3 in nl_constants.ROUTER_INTERFACE_OWNERS: self._test_fwg_with_port(device_owner_for_l3) def test_create_fwg_with_l2_port(self): device_owner_for_l2 = nl_constants.DEVICE_OWNER_COMPUTE_PREFIX + 'nova' self._test_fwg_with_port(device_owner_for_l2) def test_create_firewall_group_with_port_on_different_project(self): with self.port(tenant_id='fake_project_id_1') as port: admin_ctx = context.get_admin_context() self._create_firewall_group( self.fmt, "firewall_group1", self.DESCRIPTION, context=admin_ctx, ports=[port['port']['id']], expected_res_status=webob.exc.HTTPConflict.code, as_admin=True, ) def test_update_firewall_group_with_port_on_different_project(self): ctx = context.Context('not_admin', 'fake_project_id_1') with self.firewall_group(ctx=ctx, as_admin=True) as firewall_group: with self.port(tenant_id='fake_project_id_2') as port: data = { 'firewall_group': { 'ports': [port['port']['id']], }, } req = self.new_update_request( 'firewall_groups', data, firewall_group['firewall_group']['id'], as_admin=True, ) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_firewall_group_with_with_wrong_type_port(self): with self.port(device_owner="wrong port type") as port: self._create_firewall_group( self.fmt, "firewall_group1", self.DESCRIPTION, ports=[port['port']['id']], expected_res_status=webob.exc.HTTPConflict.code, as_admin=True, ) def test_update_firewall_group_with_with_wrong_type_port(self): with self.firewall_group(as_admin=True) as firewall_group: with self.port(device_owner="wrong port type") as port: data = { 'firewall_group': { 'ports': [port['port']['id']], }, } req = self.new_update_request( 'firewall_groups', data, firewall_group['firewall_group']['id'], ) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_firewall_group_with_router_port_already_in_use(self): with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as port: with self.firewall_group(ports=[port['port']['id']], as_admin=True): self._create_firewall_group( self.fmt, "firewall_group2", self.DESCRIPTION, ports=[port['port']['id']], expected_res_status=webob.exc.HTTPConflict.code, as_admin=True, ) def test_create_firewall_group_with_dvr_port_already_in_use(self): with self.port( device_owner=nl_constants.DEVICE_OWNER_DVR_INTERFACE) as port: with self.firewall_group(ports=[port['port']['id']], as_admin=True): self._create_firewall_group( self.fmt, "firewall_group2", self.DESCRIPTION, ports=[port['port']['id']], expected_res_status=webob.exc.HTTPConflict.code, as_admin=True, ) def test_update_firewall_group_with_port_already_in_use(self): with self.port( device_owner=nl_constants.DEVICE_OWNER_ROUTER_INTF) as port: with self.firewall_group(ports=[port['port']['id']], as_admin=True): with self.firewall_group(as_admin=True) as firewall_group: data = { 'firewall_group': { 'ports': [port['port']['id']], }, } req = self.new_update_request( 'firewall_groups', data, firewall_group['firewall_group']['id'], ) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_firewall_group_policy_rule_can_be_updated(self): pending_status = [nl_constants.PENDING_CREATE, nl_constants.PENDING_UPDATE, nl_constants.PENDING_DELETE] for status in pending_status: with self.firewall_rule(as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] with self.firewall_policy(firewall_rules=[fwr_id], as_admin=True) as fwp: fwp_id = fwp['firewall_policy']['id'] with self.firewall_group( ingress_firewall_policy_id=fwp_id) as fwg: self.plugin.driver.firewall_db.\ update_firewall_group_status( context.get_admin_context(), fwg['firewall_group']['id'], status ) data = { 'firewall_rule': { 'name': 'new_name', }, } req = self.new_update_request( 'firewall_rules', data, fwr_id, ) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPConflict.code, res.status_int) def test_create_firewall_policy_with_other_project_not_shared_rule(self): project1_context = self._get_nonadmin_context(tenant_id='project1') project2_context = self._get_nonadmin_context(tenant_id='project2') with self.firewall_rule(context=project1_context, shared=False) as fwr: fwr_id = fwr['firewall_rule']['id'] self.firewall_policy( context=project2_context, firewall_rules=[fwr_id], expected_res_status=webob.exc.HTTPNotFound.code, ) def test_update_firewall_policy_with_other_project_not_shared_rule(self): project1_context = self._get_nonadmin_context(tenant_id='project1') project2_context = self._get_nonadmin_context(tenant_id='project2') with self.firewall_rule(context=project1_context, shared=False) as fwr: with self.firewall_policy(context=project2_context, shared=False) as fwp: fwr_id = fwr['firewall_rule']['id'] fwp_id = fwp['firewall_policy']['id'] data = { 'firewall_policy': { 'firewall_rules': [fwr_id], }, } req = self.new_update_request('firewall_policy', data, fwp_id) res = req.get_response(self.ext_api) self.assertEqual(webob.exc.HTTPNotFound.code, res.status_int) def test_create_firewall_policy_with_other_project_shared_rule(self): admin_context = self._get_admin_context() project1_context = self._get_nonadmin_context(tenant_id='project1') with self.firewall_rule(context=admin_context, shared=True, as_admin=True) as fwr: fwr_id = fwr['firewall_rule']['id'] self.firewall_policy( context=project1_context, firewall_rules=[fwr_id], expected_res_status=webob.exc.HTTPOk.code, ) class TestAutomaticAssociation(TestFirewallPluginBasev2): def setUp(self): # TODO(yushiro): Replace constant value for this test class # Set auto association fwg super(TestAutomaticAssociation, self).setUp() def test_vm_port(self): port = { "id": "fake_port", "device_owner": "compute:nova", "binding:vif_type": "ovs", "binding:vif_details": {"ovs_hybrid_plug": False}, "project_id": "fake_project", "port_security_enabled": True, } self.plugin._core_plugin.get_port = mock.Mock(return_value=port) fake_default_fwg = { 'id': 'fake_id', 'name': 'default', 'ports': ['fake_port_id1'], } self.plugin.get_firewall_groups = \ mock.Mock(return_value=[fake_default_fwg]) self.plugin.update_firewall_group = mock.Mock() kwargs = { "context": mock.ANY, "port": port, "original_port": {"binding:vif_type": "unbound"} } states = (kwargs['original_port'], kwargs['port']) payload = events.DBEventPayload(mock.ANY, states=states) self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", payload=payload) self.plugin.get_firewall_groups.assert_called_once_with( mock.ANY, filters={ 'tenant_id': [kwargs['port']['project_id']], 'name': [fake_default_fwg['name']], }, fields=['id', 'ports'], ) port_ids = fake_default_fwg['ports'] + [kwargs['port']['id']] self.plugin.update_firewall_group.assert_called_once_with( mock.ANY, fake_default_fwg['id'], {'firewall_group': {'ports': port_ids}}, ) def test_vm_port_not_newly_created(self): self.plugin.get_firewall_group = mock.Mock() self.plugin.update_firewall_group = mock.Mock() # Just updated for VM port(name or description...etc.) kwargs = { "context": mock.ANY, "port": { "id": "fake_port", "device_owner": "compute:nova", "binding:vif_type": "ovs", "project_id": "fake_project" }, "original_port": { "device_owner": "compute:nova", "binding:vif_type": "ovs", "project_id": "fake_project" } } states = (kwargs['original_port'], kwargs['port']) payload = events.DBEventPayload(mock.ANY, states=states) self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", payload=payload) self.plugin.get_firewall_group.assert_not_called() self.plugin.update_firewall_group.assert_not_called() def test_not_vm_port(self): self.plugin.get_firewall_group = mock.Mock() self.plugin.update_firewall_group = mock.Mock() for device_owner in ["network:router_interface", "network:router_gateway", "network:dhcp"]: states = ({"device_owner": device_owner, "binding:vif_type": "unbound", "project_id": "fake_project"}, {"id": "fake_port", "device_owner": device_owner, "project_id": "fake_project"}) payload = events.DBEventPayload(mock.ANY, states=states) self.plugin.handle_update_port( "PORT", "after_update", "test_plugin", payload=payload) self.plugin.get_firewall_group.assert_not_called() self.plugin.update_firewall_group.assert_not_called() def test_set_port_for_default_firewall_group_raised_port_in_use(self): port_id = 'fake_port_id_already_associated_to_default_fw' port = { "id": port_id, "device_owner": "compute:nova", "binding:vif_type": "ovs", "binding:vif_details": {"ovs_hybrid_plug": False}, "project_id": "fake_project", "port_security_enabled": True, } self.plugin._core_plugin.get_port = mock.Mock(return_value=port) self.plugin.get_firewall_groups = mock.Mock(return_value=[]) self.plugin.update_firewall_group = mock.Mock( side_effect=f_exc.FirewallGroupPortInUse(port_ids=[port_id])) states = ({"binding:vif_type": "unbound"}, port) payload = events.DBEventPayload(mock.ANY, states=states) try: self.plugin.handle_update_port("PORT", "after_update", "test_plugin", payload=payload) except f_exc.FirewallGroupPortInUse: self.fail("Associating port to default firewall group raises " "'FirewallGroupPortInUse' while it should ignore it") ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/0000775000175000017500000000000000000000000024477 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/__init__.py0000664000175000017500000000000000000000000026576 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/0000775000175000017500000000000000000000000025760 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/__init__.py0000664000175000017500000000000000000000000030057 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/drivers/0000775000175000017500000000000000000000000027436 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/drivers/__init__.py0000664000175000017500000000000000000000000031535 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/0000775000175000017500000000000000000000000031241 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/__init__.py0000664000175000017500000000000000000000000033340 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/test_driver.py0000664000175000017500000000325300000000000034150 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.services.logapi.drivers import base as log_base_driver from neutron_fwaas.tests import base SUPPORTED_LOGGING_TYPES = ['firewall_group'] class FakeDriver(log_base_driver.DriverBase): @staticmethod def create(): return FakeDriver( name='fake_driver', vif_types=[], vnic_types=[], supported_logging_types=SUPPORTED_LOGGING_TYPES, requires_rpc=True ) class TestDriverBase(base.BaseTestCase): def setUp(self): super(TestDriverBase, self).setUp() self.driver = FakeDriver.create() def test_is_vif_type_compatible(self): self.assertFalse( self.driver.is_vif_type_compatible([])) def test_is_vnic_compatible(self): self.assertFalse( self.driver.is_vnic_compatible([])) def test_is_logging_type_supported(self): self.assertTrue( self.driver.is_logging_type_supported('firewall_group')) self.assertFalse( self.driver.is_logging_type_supported('security_group')) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/test_log.py0000664000175000017500000003470100000000000033440 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited. # 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 collections import defaultdict from unittest import mock from neutron.tests.unit.api.v2 import test_base from neutron_lib.services.logapi import constants as log_const from neutron_fwaas.privileged.netfilter_log import libnetfilter_log as libnflog from neutron_fwaas.services.logapi.agents.drivers.iptables import log from neutron_fwaas.tests import base FAKE_PROJECT_ID = 'fake_project_id' FAKE_PORT_ID = 'fake_port_id' FAKE_FWG_ID = 'fake_fwg_id' FAKE_LOG_ID = 'fake_log_id' FAKE_RESOURCE_TYPE = 'firewall_group' FAKE_RATE = 100 FAKE_BURST = 25 class TestLogPrefix(base.BaseTestCase): def setUp(self): super(TestLogPrefix, self).setUp() self.log_prefix = log.LogPrefix(FAKE_PORT_ID, 'fake_event', FAKE_PROJECT_ID) self.log_prefix.log_object_refs = set([FAKE_LOG_ID]) def test_add_log_obj_ref(self): added_log_id = test_base._uuid expected_log_obj_ref = set([FAKE_LOG_ID, added_log_id]) self.log_prefix.add_log_obj_ref(added_log_id) self.assertEqual(expected_log_obj_ref, self.log_prefix.log_object_refs) def test_remove_log_obj_ref(self): expected_log_obj_ref = set() self.log_prefix.remove_log_obj_ref(FAKE_LOG_ID) self.assertEqual(expected_log_obj_ref, self.log_prefix.log_object_refs) def test_is_empty(self): self.log_prefix.remove_log_obj_ref(FAKE_LOG_ID) result = self.log_prefix.is_empty self.assertEqual(True, result) class BaseIptablesLogTestCase(base.BaseTestCase): def setUp(self): super(BaseIptablesLogTestCase, self).setUp() self.iptables_manager_patch = mock.patch( 'neutron.agent.linux.iptables_manager.IptablesManager') self.iptables_manager_mock = self.iptables_manager_patch.start() resource_rpc_mock = mock.Mock() self.iptables_mock = mock.Mock() self.v4filter_mock = mock.Mock() self.v6filter_mock = mock.Mock() self.iptables_mock.ipv4 = {'filter': self.v4filter_mock} self.iptables_mock.ipv6 = {'filter': self.v6filter_mock} self.log_driver = log.IptablesLoggingDriver(mock.Mock()) self.log_driver.iptables_manager = self.iptables_mock self.log_driver.resource_rpc = resource_rpc_mock self.context = mock.Mock() self.log_driver.agent_api = mock.Mock() def test_start_logging(self): fake_router_info = mock.Mock() fake_router_info.router_id = 'fake_router_id' fake_router_info.ns_name = 'fake_namespace' libnflog.run_nflog = mock.Mock() self.log_driver._create_firewall_group_log = mock.Mock() # Test with router_info that has internal ports fake_router_info.internal_ports = [ {'id': 'fake_port1'}, {'id': 'fake_port2'}, ] fake_kwargs = { 'router_info': fake_router_info } self.log_driver.ports_belong_router = defaultdict(set) self.log_driver.start_logging(self.context, **fake_kwargs) self.log_driver._create_firewall_group_log.\ assert_called_once_with(self.context, FAKE_RESOURCE_TYPE, ports=fake_router_info.internal_ports, router_id=fake_router_info.router_id) # Test with log_resources fake_kwargs = { 'log_resources': 'fake' } self.log_driver._create_firewall_group_log.reset_mock() self.log_driver.start_logging(self.context, **fake_kwargs) self.log_driver._create_firewall_group_log. \ assert_called_once_with(self.context, FAKE_RESOURCE_TYPE, **fake_kwargs) def test_stop_logging(self): fake_kwargs = { 'log_resources': 'fake' } self.log_driver._delete_firewall_group_log = mock.Mock() self.log_driver.stop_logging(self.context, **fake_kwargs) self.log_driver._delete_firewall_group_log.\ assert_called_once_with(self.context, **fake_kwargs) fake_kwargs = { 'fake': 'fake' } self.log_driver._delete_firewall_group_log.reset_mock() self.log_driver.stop_logging(self.context, **fake_kwargs) self.log_driver._delete_firewall_group_log.assert_not_called() def test_clean_up_unused_ipt_mgrs(self): f_router_ids = ['r1', 'r2', 'r3'] self.log_driver.ipt_mgr_list = self._fake_ipt_mgr_list(f_router_ids) # Test with a port is delete from router self.log_driver.unused_port_ids = set(['r1_port1']) self.log_driver._cleanup_unused_ipt_mgrs() self.assertEqual(set(), self.log_driver.unused_port_ids) self.assertIsNone(self.log_driver.ipt_mgr_list['r1'].get('r1_port1')) # Test with all ports are deleted from router self.log_driver.unused_port_ids = set(['r2_port1', 'r2_port2']) self.log_driver._cleanup_unused_ipt_mgrs() self.assertEqual(set(), self.log_driver.unused_port_ids) self.assertIsNone(self.log_driver.ipt_mgr_list.get('r2')) def test_get_intf_name(self): fake_router = mock.Mock() fake_port_id = 'fake_router_port_id' # Test with legacy router self.log_driver.conf.agent_mode = 'legacy' fake_router.router = { 'fake': 'fake_mode' } with mock.patch.object(self.log_driver.agent_api, 'get_router_hosting_port', return_value=fake_router): intf_name = self.log_driver._get_intf_name(fake_port_id) expected_name = 'qr-fake_router' self.assertEqual(expected_name, intf_name) # Test with dvr router self.log_driver.conf.agent_mode = 'dvr_snat' fake_router.router = { 'distributed': 'fake_mode' } with mock.patch.object(self.log_driver.agent_api, 'get_router_hosting_port', return_value=fake_router): intf_name = self.log_driver._get_intf_name(fake_port_id) expected_name = 'sg-fake_router' self.assertEqual(expected_name, intf_name) # Test with fip dev self.log_driver.conf.agent_mode = 'dvr_snat' fake_router.router = { 'distributed': 'fake_mode' } fake_router.rtr_fip_connect = 'fake' self.log_driver.conf.agent_mode = 'fake' with mock.patch.object(self.log_driver.agent_api, 'get_router_hosting_port', return_value=fake_router): intf_name = self.log_driver._get_intf_name(fake_port_id) expected_name = 'rfp-fake_route' self.assertEqual(expected_name, intf_name) def test_setup_chains(self): self.log_driver._add_nflog_rules_accepted = mock.Mock() self.log_driver._add_log_rules_dropped = mock.Mock() m_ipt_mgr = mock.Mock() m_fwg_port_log = mock.Mock() # Test with ALL event m_fwg_port_log.event = log_const.ALL_EVENT self.log_driver._setup_chains(m_ipt_mgr, m_fwg_port_log) self.log_driver._add_nflog_rules_accepted.\ assert_called_once_with(m_ipt_mgr, m_fwg_port_log) self.log_driver._add_log_rules_dropped.\ assert_called_once_with(m_ipt_mgr, m_fwg_port_log) # Test with ACCEPT event self.log_driver._add_nflog_rules_accepted.reset_mock() self.log_driver._add_log_rules_dropped.reset_mock() m_fwg_port_log.event = log_const.ACCEPT_EVENT self.log_driver._setup_chains(m_ipt_mgr, m_fwg_port_log) self.log_driver._add_nflog_rules_accepted.\ assert_called_once_with(m_ipt_mgr, m_fwg_port_log) self.log_driver._add_log_rules_dropped.assert_not_called() # Test with DROP event self.log_driver._add_nflog_rules_accepted.reset_mock() self.log_driver._add_log_rules_dropped.reset_mock() m_fwg_port_log.event = log_const.DROP_EVENT self.log_driver._setup_chains(m_ipt_mgr, m_fwg_port_log) self.log_driver._add_nflog_rules_accepted.assert_not_called() self.log_driver._add_log_rules_dropped.\ assert_called_once_with(m_ipt_mgr, m_fwg_port_log) def test_add_nflog_rules_accepted(self): ipt_mgr = mock.Mock() f_accept_prefix = log.LogPrefix(FAKE_PORT_ID, log_const. ACCEPT_EVENT, FAKE_PROJECT_ID) f_port_log = self._fake_port_log('fake_log_id', log_const.ACCEPT_EVENT, FAKE_PORT_ID) self.log_driver._add_rules_to_chain_v4v6 = mock.Mock() self.log_driver._get_ipt_mgr_by_port = mock.Mock(return_value=ipt_mgr) self.log_driver._get_intf_name = mock.Mock(return_value='fake_device') with mock.patch.object(self.log_driver, '_get_prefix', side_effect=[f_accept_prefix, None]): # Test with prefix already added into prefixes_table self.log_driver._add_nflog_rules_accepted(ipt_mgr, f_port_log) self.log_driver._add_rules_to_chain_v4v6.assert_not_called() self.assertEqual(set(['fake_log_id']), f_accept_prefix.log_object_refs) # Test with prefixes_tables does not include the prefix prefix = log.LogPrefix(FAKE_PORT_ID, log_const. ACCEPT_EVENT, FAKE_PROJECT_ID) with mock.patch.object(log, 'LogPrefix', return_value=prefix): self.log_driver._add_nflog_rules_accepted(ipt_mgr, f_port_log) v4_rules, v6_rules = self._fake_nflog_rule_v4v6('fake_device', prefix.id) self.log_driver._add_rules_to_chain_v4v6.\ assert_called_once_with(ipt_mgr, 'accepted', v4_rules, v6_rules, wrap=True, top=True, tag=prefix.id) self.assertEqual(set(['fake_log_id']), prefix.log_object_refs) def test_add_nflog_rules_dropped(self): ipt_mgr = mock.Mock() f_drop_prefix = log.LogPrefix(FAKE_PORT_ID, log_const. DROP_EVENT, FAKE_PROJECT_ID) f_port_log = self._fake_port_log('fake_log_id', log_const.DROP_EVENT, FAKE_PORT_ID) self.log_driver._add_rules_to_chain_v4v6 = mock.Mock() self.log_driver._get_ipt_mgr_by_port = mock.Mock(return_value=ipt_mgr) self.log_driver._get_intf_name = mock.Mock(return_value='fake_device') with mock.patch.object(self.log_driver, '_get_prefix', side_effect=[f_drop_prefix, None]): # Test with prefix already added into prefixes_table self.log_driver._add_log_rules_dropped(ipt_mgr, f_port_log) self.log_driver._add_rules_to_chain_v4v6.assert_not_called() self.assertEqual(set(['fake_log_id']), f_drop_prefix.log_object_refs) # Test with prefixes_tables does not include the prefix prefix = log.LogPrefix(FAKE_PORT_ID, log_const. ACCEPT_EVENT, FAKE_PROJECT_ID) with mock.patch.object(log, 'LogPrefix', return_value=prefix): self.log_driver._add_log_rules_dropped(ipt_mgr, f_port_log) v4_rules, v6_rules = self._fake_nflog_rule_v4v6('fake_device', prefix.id) calls = [ mock.call(ipt_mgr, 'dropped', v4_rules, v6_rules, wrap=True, top=True, tag=prefix.id), mock.call(ipt_mgr, 'rejected', v4_rules, v6_rules, wrap=True, top=True, tag=prefix.id), ] self.log_driver._add_rules_to_chain_v4v6.\ assert_has_calls(calls) self.assertEqual(set(['fake_log_id']), prefix.log_object_refs) def _fake_port_log(self, log_id, event, port_id): f_log_info = { 'event': event, 'project_id': FAKE_PROJECT_ID, 'id': log_id } return log.FWGPortLog(port_id, f_log_info) def _fake_nflog_rule_v4v6(self, device, tag): v4_nflog_rule = ['-i %s -m limit --limit %s/s --limit-burst %s ' '-j NFLOG --nflog-prefix %s' % (device, FAKE_RATE, FAKE_BURST, tag)] v4_nflog_rule += ['-o %s -m limit --limit %s/s --limit-burst %s ' '-j NFLOG --nflog-prefix %s' % (device, FAKE_RATE, FAKE_BURST, tag)] v6_nflog_rule = ['-i %s -m limit --limit %s/s --limit-burst %s ' '-j NFLOG --nflog-prefix %s' % (device, FAKE_RATE, FAKE_BURST, tag)] v6_nflog_rule += ['-o %s -m limit --limit %s/s --limit-burst %s ' '-j NFLOG --nflog-prefix %s' % (device, FAKE_RATE, FAKE_BURST, tag)] return v4_nflog_rule, v6_nflog_rule def _fake_ipt_mgr_list(self, router_ids): f_ipt_mgrs = defaultdict(dict) for router_id in router_ids: f_port_id1 = router_id + '_port1' f_port_id2 = router_id + '_port2' ipt_mgr = mock.Mock() ipt_mgr.ns_name = 'ns_' + router_id f_ipt_mgrs[router_id][f_port_id1] = ipt_mgr f_ipt_mgrs[router_id][f_port_id2] = ipt_mgr return f_ipt_mgrs ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/l3/0000775000175000017500000000000000000000000026276 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/l3/__init__.py0000664000175000017500000000000000000000000030375 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/agents/l3/test_fwg_log.py0000664000175000017500000000423600000000000031340 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited. # 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.api.rpc.callbacks.consumer import registry from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import resources_rpc from neutron.tests.unit.services.logapi.agent.l3 import test_base as base from neutron_lib import constants as lib_const from neutron_fwaas.services.logapi.agents.l3 import fwg_log class FWaaSL3LoggingExtensionInitializeTestCase(base.L3LoggingExtBaseTestCase): def setUp(self): super(FWaaSL3LoggingExtensionInitializeTestCase, self).setUp() self.fw_l3_log_ext = fwg_log.FWaaSL3LoggingExtension() self.fw_l3_log_ext.consume_api(self.agent_api) @mock.patch.object(registry, 'register') @mock.patch.object(resources_rpc, 'ResourcesPushRpcCallback') def test_initialize_subscribed_to_rpc(self, rpc_mock, subscribe_mock): call_to_patch = 'neutron_lib.rpc.Connection' with mock.patch(call_to_patch, return_value=self.connection) as create_connection: self.fw_l3_log_ext.initialize( self.connection, lib_const.L3_AGENT_MODE) create_connection.assert_has_calls([mock.call()]) self.connection.create_consumer.assert_has_calls( [mock.call( resources_rpc.resource_type_versioned_topic( resources.LOGGING_RESOURCE), [rpc_mock()], fanout=True)] ) subscribe_mock.assert_called_with( mock.ANY, resources.LOGGING_RESOURCE) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/base.py0000664000175000017500000000306500000000000025767 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 unittest import mock from neutron.api.rpc.callbacks.consumer import registry as cons_registry from neutron.api.rpc.callbacks.producer import registry as prod_registry from neutron.api.rpc.callbacks import resource_manager from neutron.tests.unit import testlib_api class BaseLogTestCase(testlib_api.SqlTestCase): def setUp(self): super(BaseLogTestCase, self).setUp() with mock.patch.object( resource_manager.ResourceCallbacksManager, '_singleton', new_callable=mock.PropertyMock(return_value=False)): self.cons_mgr = resource_manager.ConsumerResourceCallbacksManager() self.prod_mgr = resource_manager.ProducerResourceCallbacksManager() for mgr in (self.cons_mgr, self.prod_mgr): mgr.clear() mock.patch.object( cons_registry, '_get_manager', return_value=self.cons_mgr).start() mock.patch.object( prod_registry, '_get_manager', return_value=self.prod_mgr).start() ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/common/0000775000175000017500000000000000000000000025767 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/common/__init__.py0000664000175000017500000000000000000000000030066 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/common/test_fwg_callback.py0000664000175000017500000002301500000000000032000 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects import ports as port_objects from neutron.services.logapi.drivers import base as log_driver_base from neutron.services.logapi.drivers import manager as driver_mgr from neutron.tests import base from neutron_lib.callbacks import events from neutron_lib.callbacks import registry from neutron_lib import constants as nl_const from neutron_fwaas.common import fwaas_constants as fw_const from neutron_fwaas.services.logapi.common import fwg_callback from neutron_fwaas.services.logapi.common import log_db_api FAKE_DRIVER = None class FakeDriver(log_driver_base.DriverBase): @staticmethod def create(): return FakeDriver( name='fake_driver', vif_types=[], vnic_types=[], supported_logging_types=['firewall_group'], requires_rpc=True ) def fake_register(): global FAKE_DRIVER if not FAKE_DRIVER: FAKE_DRIVER = FakeDriver.create() driver_mgr.register(fw_const.FIREWALL_GROUP, fwg_callback.FirewallGroupCallBack) class TestFirewallGroupRuleCallback(base.BaseTestCase): def setUp(self): super(TestFirewallGroupRuleCallback, self).setUp() self.driver_manager = driver_mgr.LoggingServiceDriverManager() self.fwg_callback = fwg_callback.FirewallGroupCallBack(mock.Mock(), mock.Mock()) self.m_context = mock.Mock() @mock.patch.object(fwg_callback.FirewallGroupCallBack, 'handle_event') def test_handle_event(self, mock_fwg_cb): fake_register() self.driver_manager.register_driver(FAKE_DRIVER) registry.publish( fw_const.FIREWALL_GROUP, events.AFTER_CREATE, mock.ANY) mock_fwg_cb.assert_called_once_with( fw_const.FIREWALL_GROUP, events.AFTER_CREATE, mock.ANY, payload=None) mock_fwg_cb.reset_mock() registry.publish( fw_const.FIREWALL_GROUP, events.AFTER_UPDATE, mock.ANY) mock_fwg_cb.assert_called_once_with( fw_const.FIREWALL_GROUP, events.AFTER_UPDATE, mock.ANY, payload=None) mock_fwg_cb.reset_mock() registry.publish( 'non_registered_resource', events.AFTER_CREATE, mock.ANY) mock_fwg_cb.assert_not_called() mock_fwg_cb.reset_mock() registry.publish( 'non_registered_resource', events.AFTER_UPDATE, mock.ANY) mock_fwg_cb.assert_not_called() def test_need_to_notify(self): port_objects.Port.get_object = \ mock.Mock(side_effect=self._get_object_side_effect) # Test with router devices for device in nl_const.ROUTER_INTERFACE_OWNERS: result = self.fwg_callback.need_to_notify(self.m_context, [device]) self.assertEqual(True, result) # Test with non-router device result = self.fwg_callback.need_to_notify(self.m_context, ['fake_port']) self.assertEqual(False, result) # Test with ports_delta is empty result = self.fwg_callback.need_to_notify(self.m_context, []) self.assertEqual(False, result) def test_trigger_logging(self): m_payload = mock.Mock() self.fwg_callback.resource_push_api = mock.Mock() m_payload.resource_id = 'fake_resource_id' ports_delta = ['fake_port_id'] # Test with log resource could be found from DB with mock.patch.object(log_db_api, 'get_logs_for_fwg', return_value={'fake': 'fake'}): self.fwg_callback.trigger_logging(self.m_context, m_payload.resource_id, ports_delta) self.fwg_callback.resource_push_api.assert_called() # Test with log resource could not be found from DB self.fwg_callback.resource_push_api.reset_mock() with mock.patch.object(log_db_api, 'get_logs_for_fwg', return_value={}): self.fwg_callback.trigger_logging(self.m_context, m_payload.resource_id, ports_delta) self.fwg_callback.resource_push_api.assert_not_called() def _get_object_side_effect(self, context, id): fake_port = { 'id': 'fake_id', 'device_owner': id, } return fake_port def test_handle_event_with_router_port(self): with mock.patch.object(self.fwg_callback, 'need_to_notify', return_value=True): with mock.patch.object(self.fwg_callback, 'trigger_logging'): # Test for firewall group creation with router port m_payload = self._mock_payload(events.AFTER_CREATE, 'fake_port_id') self.fwg_callback.handle_event(mock.ANY, events.AFTER_CREATE, mock.ANY, **{'payload': m_payload}) self.fwg_callback.trigger_logging.assert_called() # Test for firewall group update with router port self.fwg_callback.trigger_logging.reset_mock() m_payload = self._mock_payload(events.AFTER_UPDATE, 'fake_port_id') self.fwg_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, **{'payload': m_payload}) self.fwg_callback.trigger_logging.assert_called() def test_handle_event_with_non_router_port(self): with mock.patch.object(self.fwg_callback, 'need_to_notify', return_value=False): with mock.patch.object(self.fwg_callback, 'trigger_logging'): # Test for firewall group creation with non router ports m_payload = self._mock_payload(events.AFTER_CREATE, 'fake_port_id') self.fwg_callback.handle_event(mock.ANY, events.AFTER_CREATE, mock.ANY, **{'payload': m_payload}) self.fwg_callback.trigger_logging.assert_not_called() # Test for firewall group creation without ports self.fwg_callback.trigger_logging.reset_mock() m_payload = self._mock_payload(events.AFTER_CREATE) self.fwg_callback.handle_event(mock.ANY, events.AFTER_CREATE, mock.ANY, **{'payload': m_payload}) self.fwg_callback.trigger_logging.assert_not_called() # Test for firewall group update with non router ports self.fwg_callback.trigger_logging.reset_mock() m_payload = self._mock_payload(events.AFTER_UPDATE, 'fake_port_id') self.fwg_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, **{'payload': m_payload}) self.fwg_callback.trigger_logging.assert_not_called() # Test for firewall group update without ports self.fwg_callback.trigger_logging.reset_mock() m_payload = self._mock_payload(events.AFTER_UPDATE) self.fwg_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, **{'payload': m_payload}) self.fwg_callback.trigger_logging.assert_not_called() def _mock_payload(self, event, ports_delta=None): m_payload = mock.Mock() m_payload.context = self.m_context if event == events.AFTER_CREATE: if ports_delta: m_payload.latest_state = { 'ports': [ports_delta] } else: m_payload.latest_state = { 'ports': [] } if event == events.AFTER_UPDATE: if ports_delta: m_payload.states = [ {'ports': [ports_delta]}, {'ports': []} ] else: m_payload.states = [ {'ports': []}, {'ports': []} ] return m_payload ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/common/test_log_db_api.py0000664000175000017500000003461400000000000031467 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects.logapi import logging_resource as log_object from neutron.objects import ports as port_objects from neutron.services.logapi.rpc import server as server_rpc from neutron.tests import base from neutron_lib import constants as nl_const from oslo_utils import uuidutils from neutron_fwaas.services.logapi.common import log_db_api from neutron_fwaas.services.logapi.rpc import log_server as fwg_rpc FWG = 'firewall_group' def _create_log_object(tenant_id, resource_id=None, target_id=None, event='ALL'): log_data = { 'id': uuidutils.generate_uuid(), 'name': 'fake_log_name', 'resource_type': FWG, 'project_id': tenant_id, 'event': event, 'enabled': True} if resource_id: log_data['resource_id'] = resource_id if target_id: log_data['target_id'] = target_id return log_object.Log(**log_data) def _fake_log_info(id, project_id, ports_id, event='ALL'): expected = { 'id': id, 'project_id': project_id, 'ports_log': ports_id, 'event': event } return expected def _fake_port_object(port_id, device_owner, status, project_id=uuidutils.generate_uuid()): port_data = { 'id': port_id, 'device_owner': device_owner, 'project_id': project_id } if status: port_data['status'] = status return port_data class LoggingRpcCallbackTestCase(base.BaseTestCase): def setUp(self): super(LoggingRpcCallbackTestCase, self).setUp() self.context = mock.Mock() self.rpc_callback = server_rpc.LoggingApiSkeleton() log_db_api.fw_plugin_db = mock.Mock() self.vm_port = uuidutils.generate_uuid() self.router_port = uuidutils.generate_uuid() self.fake_vm_port = \ _fake_port_object(self.vm_port, nl_const.DEVICE_OWNER_COMPUTE_PREFIX, nl_const.PORT_STATUS_ACTIVE) self.fake_router_port = \ _fake_port_object(self.router_port, nl_const.DEVICE_OWNER_ROUTER_INTF, nl_const.PORT_STATUS_ACTIVE) self.fake_router_ports = \ [_fake_port_object(self.router_port, device, nl_const.PORT_STATUS_ACTIVE) for device in nl_const.ROUTER_INTERFACE_OWNERS] def test_get_fwg_log_info_for_log_resources(self): fwg_id = uuidutils.generate_uuid() tenant_id = uuidutils.generate_uuid() log_obj = _create_log_object(tenant_id, resource_id=fwg_id) rpc_call = fwg_rpc.get_fwg_log_info_for_log_resources with mock.patch.object(server_rpc, 'get_rpc_method', return_value=rpc_call): fake_ports = ['fake_port_1', 'fake_port_2'] with mock.patch.object(log_db_api, '_get_ports_being_logged', return_value=fake_ports): expected_log_info = [ _fake_log_info(log_obj['id'], tenant_id, fake_ports) ] logs_info = self.rpc_callback.\ get_sg_log_info_for_log_resources(self.context, resource_type=FWG, log_resources=[log_obj]) self.assertEqual(expected_log_info, logs_info) def test_get_fwg_log_info_for_port(self): fwg_id = uuidutils.generate_uuid() port_id = uuidutils.generate_uuid() tenant_id = uuidutils.generate_uuid() log_obj = _create_log_object(tenant_id, resource_id=fwg_id, target_id=port_id) rpc_call = fwg_rpc.get_fwg_log_info_for_port with mock.patch.object(server_rpc, 'get_rpc_method', return_value=rpc_call): with mock.patch.object(log_db_api, 'get_logs_for_port', return_value=[log_obj]): fake_ports = [port_id, 'fake_port2'] with mock.patch.object(log_db_api, '_get_ports_being_logged', return_value=fake_ports): expected_log_info = [_fake_log_info(log_obj['id'], tenant_id, fake_ports)] logs_info = self.rpc_callback.\ get_sg_log_info_for_port(self.context, resource_type=FWG, port_id=port_id) self.assertEqual(expected_log_info, logs_info) def test_get_ports_being_logged_with_target_id(self): tenant_id = uuidutils.generate_uuid() fwg_id = uuidutils.generate_uuid() # Test with VM port log_obj = _create_log_object(tenant_id, resource_id=fwg_id, target_id=self.vm_port) with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_vm_port): logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual([], logged_port_ids) # Test with router ports log_obj = _create_log_object(tenant_id, resource_id=fwg_id, target_id=self.router_port) log_db_api.fw_plugin_db. \ get_fwg_attached_to_port = mock.Mock(return_value='fwg_id') with mock.patch.object(port_objects.Port, 'get_object', side_effect=self.fake_router_ports): for port in self.fake_router_ports: logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual([self.router_port], logged_port_ids) # Test with inactive router port self.fake_router_port['status'] = nl_const.PORT_STATUS_DOWN log_obj = _create_log_object(tenant_id, resource_id=fwg_id, target_id=self.router_port) log_db_api.fw_plugin_db. \ get_fwg_attached_to_port = mock.Mock(return_value='fwg_id') with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_router_port): logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual([], logged_port_ids) def test_get_ports_being_logged_with_resource_id(self): tenant_id = uuidutils.generate_uuid() fwg_id = uuidutils.generate_uuid() log_obj = _create_log_object(tenant_id, resource_id=fwg_id) log_db_api.fw_plugin_db.get_ports_in_firewall_group = \ mock.Mock(return_value=[self.vm_port]) # Test with VM port with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_vm_port): logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual([], logged_port_ids) # Test with router ports router_ports = [self.router_port, self.router_port, self.router_port] log_db_api.fw_plugin_db. \ get_ports_in_firewall_group = mock.Mock(return_value=router_ports) log_db_api.fw_plugin_db. \ get_fwg_attached_to_port = mock.Mock(return_value='fwg_id') with mock.patch.object(port_objects.Port, 'get_object', side_effect=self.fake_router_ports): logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual(router_ports, logged_port_ids) # Test with both vm port and router ports log_db_api.fw_plugin_db.get_ports_in_firewall_group = \ mock.Mock(return_value=[self.vm_port, self.router_port]) log_db_api.fw_plugin_db. \ get_fwg_attached_to_port = mock.Mock(return_value='fwg_id') with mock.patch.object(port_objects.Port, 'get_object', side_effect=[self.fake_vm_port, self.fake_router_port]): logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual([self.router_port], logged_port_ids) # Test with inactive router port log_db_api.fw_plugin_db.get_ports_in_firewall_group = \ mock.Mock(return_value=[self.router_port]) log_db_api.fw_plugin_db. \ get_fwg_attached_to_port = mock.Mock(return_value='fwg_id') with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_router_port): logged_port_ids = \ log_db_api._get_ports_being_logged(self.context, log_obj) self.assertEqual([self.router_port], logged_port_ids) def test_get_ports_being_logged_with_ports_in_tenant(self): tenant_id = uuidutils.generate_uuid() log_obj = _create_log_object(tenant_id) log_db_api.fw_plugin_db.get_fwg_ports_in_tenant = \ mock.Mock(return_value=[self.router_port]) log_db_api.fw_plugin_db. \ get_fwg_attached_to_port = mock.Mock(return_value='fwg_id') with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_router_port): log_db_api._get_ports_being_logged(self.context, log_obj) log_db_api.fw_plugin_db.get_fwg_ports_in_tenant.\ assert_called_with(self.context, tenant_id) def test_logs_for_port_with_vm_port(self): with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_vm_port): logs = log_db_api.get_logs_for_port(self.context, self.vm_port) self.assertEqual([], logs) def test_logs_for_port_with_router_port(self): tenant_id = uuidutils.generate_uuid() resource_id = uuidutils.generate_uuid() target_id = uuidutils.generate_uuid() log_db_api.fw_plugin_db.get_fwg_attached_to_port = \ mock.Mock(side_effect=[[], resource_id, resource_id]) with mock.patch.object(port_objects.Port, 'get_object', return_value=self.fake_router_port): # Test with router port that did not attach to fwg logs = log_db_api.get_logs_for_port(self.context, self.router_port) self.assertEqual([], logs) # Test with router port that attached to fwg # Fake log objects that bounds a given port log = _create_log_object(tenant_id) resource_log = _create_log_object(tenant_id, resource_id) target_log = _create_log_object(tenant_id, resource_id, target_id) log_objs = [log, target_log, resource_log] with mock.patch.object(log_object.Log, 'get_objects', return_value=log_objs): self.fake_router_port = mock.Mock(return_value=target_id) logs = log_db_api.get_logs_for_port(self.context, self.router_port) self.assertEqual(log_objs, logs) # Fake log objects that does not bound a given port unbound_resource = uuidutils.generate_uuid() resource_log = _create_log_object(tenant_id, unbound_resource) target_log = _create_log_object(tenant_id, unbound_resource, target_id) log_objs = [log, target_log, resource_log] with mock.patch.object(log_object.Log, 'get_objects', return_value=log_objs): self.fake_router_port = mock.Mock(return_value=target_id) logs = log_db_api.get_logs_for_port(self.context, self.router_port) self.assertEqual([log], logs) def test_logs_for_fwg(self): tenant_id = uuidutils.generate_uuid() resource_id = uuidutils.generate_uuid() target_id = uuidutils.generate_uuid() # Fake log objects that bounds a given fwg log = _create_log_object(tenant_id) resource_log = _create_log_object(tenant_id, resource_id) target_log = _create_log_object(tenant_id, target_id=target_id) ports_delta = [target_id] # Test with port that in ports_delta log_db_api.fw_plugin_db.get_fwg_attached_to_port = \ mock.Mock(return_value=None) with mock.patch.object(log_object.Log, 'get_objects', return_value=[target_log]): logs = log_db_api.get_logs_for_fwg(self.context, resource_id, ports_delta) self.assertEqual([target_log], logs) # Test with log that bound to a give fwg with mock.patch.object(log_object.Log, 'get_objects', return_value=[resource_log]): logs = log_db_api.get_logs_for_fwg(self.context, resource_id, ports_delta) self.assertEqual([resource_log], logs) # Test with log that does not bound to any fwg or port with mock.patch.object(log_object.Log, 'get_objects', return_value=[log]): logs = log_db_api.get_logs_for_fwg(self.context, resource_id, ports_delta) self.assertEqual([log], logs) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/common/test_port_callback.py0000664000175000017500000002071000000000000032200 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects import ports as port_objects from neutron.services.logapi.drivers import base as log_driver_base from neutron.services.logapi.drivers import manager as driver_mgr 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 nl_const from oslo_utils import uuidutils from neutron_fwaas.services.logapi.common import log_db_api from neutron_fwaas.services.logapi.common import port_callback FAKE_DRIVER = None class FakeDriver(log_driver_base.DriverBase): @staticmethod def create(): return FakeDriver( name='fake_driver', vif_types=[], vnic_types=[], supported_logging_types=['firewall_group'], requires_rpc=True ) def fake_register(): global FAKE_DRIVER if not FAKE_DRIVER: FAKE_DRIVER = FakeDriver.create() driver_mgr.register(resources.PORT, port_callback.NeutronPortCallBack) class TestFirewallGroupRuleCallback(base.BaseTestCase): def setUp(self): super(TestFirewallGroupRuleCallback, self).setUp() self.driver_manager = driver_mgr.LoggingServiceDriverManager() self.port_callback = port_callback.NeutronPortCallBack(mock.Mock(), mock.Mock()) self.m_context = mock.Mock() def _create_port_object(self, name=None, device_owner=None, status=nl_const.PORT_STATUS_ACTIVE): port_data = { 'id': uuidutils.generate_uuid(), 'project_id': 'fake_tenant_id', 'status': status } if name: port_data['name'] = name if device_owner: port_data['device_owner'] = device_owner return port_objects.Port(**port_data) @mock.patch.object(port_callback.NeutronPortCallBack, 'handle_event') def test_handle_event(self, m_port_cb_handler): fake_register() self.driver_manager.register_driver(FAKE_DRIVER) payload = events.DBEventPayload(None) registry.publish(resources.PORT, events.AFTER_CREATE, mock.ANY, payload) m_port_cb_handler.assert_called_once_with( resources.PORT, events.AFTER_CREATE, mock.ANY, payload=payload) m_port_cb_handler.reset_mock() registry.publish( resources.PORT, events.AFTER_UPDATE, mock.ANY, payload) m_port_cb_handler.assert_called_once_with( resources.PORT, events.AFTER_UPDATE, mock.ANY, payload=payload) m_port_cb_handler.reset_mock() registry.publish( 'non_registered_resource', events.AFTER_CREATE, mock.ANY) m_port_cb_handler.assert_not_called() m_port_cb_handler.reset_mock() registry.publish( 'non_registered_resource', events.AFTER_UPDATE, mock.ANY) m_port_cb_handler.assert_not_called() def test_trigger_logging(self): fake_log_obj = mock.Mock() self.port_callback.resource_push_api = mock.Mock() port = self._create_port_object(device_owner='fake_device_owner') # Test with log resource could be found from DB with mock.patch.object(log_db_api, 'get_logs_for_port', return_value=[fake_log_obj]): self.port_callback.trigger_logging(self.m_context, port) self.port_callback.resource_push_api.assert_called() # Test with log resource could not be found from DB self.port_callback.resource_push_api.reset_mock() with mock.patch.object(log_db_api, 'get_logs_for_port', return_value=[]): self.port_callback.trigger_logging(self.m_context, port) self.port_callback.resource_push_api.assert_not_called() def test_handle_event_with_router_port(self): with mock.patch.object(self.port_callback, 'trigger_logging'): # Test for router port enabling payload = self._fake_port_config( nl_const.DEVICE_OWNER_ROUTER_INTF, action='enable') self.port_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, payload=payload) self.port_callback.trigger_logging.assert_called() # Test for router port disabling self.port_callback.trigger_logging.reset_mock() payload = self._fake_port_config( nl_const.DEVICE_OWNER_ROUTER_INTF, action='disable') self.port_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, payload=payload) self.port_callback.trigger_logging.assert_called() # Test for router port status does not change self.port_callback.trigger_logging.reset_mock() payload = \ self._fake_port_config(nl_const.DEVICE_OWNER_ROUTER_INTF) self.port_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, payload=payload) self.port_callback.trigger_logging.assert_not_called() def test_handle_event_with_non_router_port(self): with mock.patch.object(self.port_callback, 'trigger_logging'): # Test for port enabling payload = self._fake_port_config('fake_port_type', action='enable') self.port_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, payload=payload) self.port_callback.trigger_logging.assert_not_called() # Test for port disabling self.port_callback.trigger_logging.reset_mock() payload = self._fake_port_config('fake_port_type', action='disable') self.port_callback.handle_event(mock.ANY, events.AFTER_UPDATE, mock.ANY, payload=payload) self.port_callback.trigger_logging.assert_not_called() def _fake_port_config(self, device_owner, action=None): if action == 'enable': # Create original port with DOWN status original_port = self._create_port_object( device_owner=device_owner, status=nl_const.PORT_STATUS_DOWN) # Create port with ACTIVE status port = self._create_port_object( device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE) elif action == 'disable': # Create original port with ACTIVE status original_port = self._create_port_object( device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE) # Create port with DOWN status port = self._create_port_object( device_owner=device_owner, status=nl_const.PORT_STATUS_DOWN) else: # Create original port with ACTIVE status original_port = self._create_port_object( device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE) # Create port with ACTIVE status port = self._create_port_object( device_owner=device_owner, status=nl_const.PORT_STATUS_ACTIVE) payload = events.DBEventPayload(self.m_context, states=[original_port, port]) return payload ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/rpc/0000775000175000017500000000000000000000000025263 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/rpc/__init__.py0000664000175000017500000000000000000000000027362 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/rpc/test_log_server.py0000664000175000017500000000447500000000000031055 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.services.logapi.rpc import server as server_rpc from neutron.tests import base from neutron_fwaas.services.logapi.rpc import log_server as fw_server_rpc class FWGLoggingApiSkeletonTestCase(base.BaseTestCase): @mock.patch("neutron_fwaas.services.logapi.common.log_db_api." "get_fwg_log_info_for_port") def test_get_fwg_log_info_for_port(self, mock_callback): with mock.patch.object( server_rpc, 'get_rpc_method', return_value=fw_server_rpc.get_fwg_log_info_for_port ): test_obj = server_rpc.LoggingApiSkeleton() m_context = mock.Mock() port_id = '123' test_obj.get_sg_log_info_for_port(m_context, resource_type='firewall_v2', port_id=port_id) mock_callback.assert_called_with(m_context, port_id) @mock.patch("neutron_fwaas.services.logapi.common.log_db_api." "get_fwg_log_info_for_log_resources") def test_get_fwg_log_info_for_log_resources(self, mock_callback): with mock.patch.object( server_rpc, 'get_rpc_method', return_value=fw_server_rpc.get_fwg_log_info_for_log_resources ): test_obj = server_rpc.LoggingApiSkeleton() m_context = mock.Mock() log_resources = [mock.Mock()] test_obj.get_sg_log_info_for_log_resources( m_context, resource_type='firewall_v2', log_resources=log_resources) mock_callback.assert_called_with(m_context, log_resources) ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/tests/unit/services/logapi/test_fwg_validate.py0000664000175000017500000001524500000000000030553 0ustar00zuulzuul00000000000000# Copyright (c) 2018 Fujitsu Limited # 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.objects import ports from neutron.services.logapi.common import exceptions as log_exc from neutron.services.logapi.common import validators from neutron.tests import base from neutron_lib import constants as nl_const from sqlalchemy.orm import exc as orm_exc from neutron_fwaas.services.logapi import exceptions as fwg_log_exc from neutron_fwaas.services.logapi import fwg_validate class TestFWGLogRequestValidations(base.BaseTestCase): """Test validator for a log creation request""" def setUp(self): super(TestFWGLogRequestValidations, self).setUp() fwg_validate.fwg_plugin = mock.Mock() fwg_validate.fwg_plugin.driver = mock.Mock() fwg_validate.fwg_plugin.driver.firewall_db = mock.Mock() def test_validate_fwg_request(self): m_context = mock.Mock() fake_data = { 'resource_type': 'firewall_group', 'resource_id': 'fake_fwg_id' } with mock.patch.object(fwg_validate, '_check_fwg'): fwg_validate.validate_firewall_group_request(m_context, fake_data) fwg_validate._check_fwg.\ assert_called_with(m_context, fake_data['resource_id']) fake_data = { 'resource_type': 'firewall_group', 'resource_id': 'fake_fwg_id', 'target_id': 'fake_port_id' } with mock.patch.object(fwg_validate, '_check_target_resource_bound_fwg'): with mock.patch.object(fwg_validate, '_check_fwg'): with mock.patch.object(fwg_validate, '_check_fwg_port'): fwg_validate.validate_firewall_group_request(m_context, fake_data) fwg_validate._check_target_resource_bound_fwg.\ assert_called_with(m_context, fake_data['resource_id'], fake_data['target_id']) fwg_validate._check_fwg. \ assert_called_with(m_context, fake_data['resource_id']) fwg_validate._check_fwg_port. \ assert_called_with(m_context, fake_data['target_id']) def test_validate_request_fwg_id_not_exists(self): with mock.patch.object(fwg_validate.fwg_plugin, 'get_firewall_group', side_effect=orm_exc.NoResultFound): self.assertRaises( log_exc.ResourceNotFound, fwg_validate._check_fwg, mock.ANY, 'fake_fwg_id') def test_validate_request_fwg_not_active(self): fake_fwg = {'id': '1234', 'status': 'PENDING'} with mock.patch.object(fwg_validate.fwg_plugin, 'get_firewall_group', return_value=fake_fwg): self.assertRaises( fwg_log_exc.FWGIsNotReadyForLogging, fwg_validate._check_fwg, mock.ANY, 'fake_fwg_id') def test_validate_request_router_or_port_id_not_exists(self): with mock.patch.object(ports.Port, 'get_object', return_value=None): self.assertRaises( log_exc.TargetResourceNotFound, fwg_validate._check_fwg_port, mock.ANY, 'fake_port_id') def test_validate_request_unsupported_fwg_log_on_vm_port(self): fake_port = {'device_owner': "compute:"} with mock.patch.object(ports.Port, 'get_object', return_value=fake_port): with mock.patch.object(validators, 'validate_log_type_for_port', return_value=False): self.assertRaises( log_exc.LoggingTypeNotSupported, fwg_validate._check_fwg_port, mock.ANY, 'fake_port_id') def test_validate_request_router_port_is_not_active(self): non_active_status = [nl_const.PORT_STATUS_DOWN, nl_const.PORT_STATUS_ERROR, nl_const.PORT_STATUS_NOTAPPLICABLE, nl_const.PORT_STATUS_BUILD] fake_port = [{'device_owner': nl_const.DEVICE_OWNER_ROUTER_INTF, 'status': status} for status in non_active_status] with mock.patch.object(ports.Port, 'get_object', side_effect=fake_port): for status in non_active_status: self.assertRaises( fwg_log_exc.PortIsNotReadyForLogging, fwg_validate._check_fwg_port, mock.ANY, 'fake_port_id') def test_validate_request_router_port_was_not_associated_fwg(self): fake_port = {'device_owner': nl_const.DEVICE_OWNER_ROUTER_INTF, 'status': nl_const.PORT_STATUS_ACTIVE} with mock.patch.object(ports.Port, 'get_object', return_value=fake_port): with mock.patch.object(fwg_validate.fwg_plugin.driver.firewall_db, 'get_fwg_attached_to_port', return_value=None): self.assertRaises( fwg_log_exc.TargetResourceNotAssociated, fwg_validate._check_fwg_port, mock.ANY, 'fake_port_id') def test_validate_request_target_resource_not_bound_fwg(self): fake_ports_in_fwg = ['fake_port_id1, fake_port_id2'] with mock.patch.object( fwg_validate.fwg_plugin.driver.firewall_db, 'get_ports_in_firewall_group', return_value=fake_ports_in_fwg): self.assertRaises( log_exc.InvalidResourceConstraint, fwg_validate._check_target_resource_bound_fwg, mock.ANY, mock.ANY, 'fake_target_id') ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/neutron_fwaas/version.py0000664000175000017500000000126400000000000021322 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-fwaas') ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2893503 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/0000775000175000017500000000000000000000000020752 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/PKG-INFO0000664000175000017500000000410700000000000022051 0ustar00zuulzuul00000000000000Metadata-Version: 1.2 Name: neutron-fwaas Version: 20.0.0 Summary: OpenStack Networking FWaaS Home-page: https://docs.openstack.org/neutron-fwaas/latest/ Author: OpenStack Author-email: openstack-discuss@lists.openstack.org License: UNKNOWN Description: ======================== Team and repository tags ======================== .. image:: https://governance.openstack.org/tc/badges/neutron-fwaas.svg :target: https://governance.openstack.org/tc/reference/tags/index.html .. Change things from this point on Welcome! ======== This package contains the code for the Neutron Firewall as a Service (FWaaS) service. This package requires Neutron to run. External 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 git.openstack.org at: . Please refer to Neutron documentation for more information: `Neutron README.rst `_ Get release notes: `Neutron FWaaS Release Notes `_ Platform: UNKNOWN Classifier: Environment :: OpenStack Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: System Administrators Classifier: License :: OSI Approved :: Apache Software License Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3 :: Only Requires-Python: >=3.8 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/SOURCES.txt0000664000175000017500000003756700000000000022660 0ustar00zuulzuul00000000000000.coveragerc .mailmap .pylintrc .stestr.conf .zuul.yaml AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE README.rst TESTING.rst babel.cfg bindep.txt requirements.txt setup.cfg setup.py test-requirements.txt tox.ini devstack/README.rst devstack/plugin.sh devstack/settings devstack/lib/l2_agent devstack/lib/l3_agent doc/requirements.txt doc/source/conf.py doc/source/index.rst doc/source/_static/.placeholder doc/source/configuration/fwaas_driver.rst doc/source/configuration/index.rst doc/source/configuration/neutron_fwaas.rst doc/source/configuration/policy-sample.rst doc/source/configuration/policy.rst doc/source/configuration/samples/fwaas_driver.rst doc/source/configuration/samples/neutron_fwaas.rst doc/source/contributor/contributing.rst doc/source/contributor/devstack.rst doc/source/contributor/fwaas_v2.rst doc/source/contributor/index.rst doc/source/contributor/modules.rst doc/source/install/index.rst etc/README.txt etc/neutron/rootwrap.d/fwaas-privsep.filters etc/oslo-config-generator/fwaas_driver.ini etc/oslo-config-generator/neutron_fwaas.conf etc/oslo-policy-generator/policy.conf neutron_fwaas/__init__.py neutron_fwaas/_i18n.py neutron_fwaas/opts.py neutron_fwaas/version.py neutron_fwaas.egg-info/PKG-INFO neutron_fwaas.egg-info/SOURCES.txt neutron_fwaas.egg-info/dependency_links.txt neutron_fwaas.egg-info/entry_points.txt neutron_fwaas.egg-info/not-zip-safe neutron_fwaas.egg-info/pbr.json neutron_fwaas.egg-info/requires.txt neutron_fwaas.egg-info/top_level.txt neutron_fwaas/cmd/__init__.py neutron_fwaas/cmd/v1_to_v2_db_migration.py neutron_fwaas/cmd/upgrade_checks/__init__.py neutron_fwaas/cmd/upgrade_checks/checks.py neutron_fwaas/common/__init__.py neutron_fwaas/common/exceptions.py neutron_fwaas/common/fwaas_constants.py neutron_fwaas/common/resources.py neutron_fwaas/db/__init__.py neutron_fwaas/db/firewall/__init__.py neutron_fwaas/db/firewall/firewall_db.py neutron_fwaas/db/firewall/v2/__init__.py neutron_fwaas/db/firewall/v2/firewall_db_v2.py neutron_fwaas/db/migration/__init__.py neutron_fwaas/db/migration/alembic_migrations/README neutron_fwaas/db/migration/alembic_migrations/__init__.py neutron_fwaas/db/migration/alembic_migrations/env.py neutron_fwaas/db/migration/alembic_migrations/script.py.mako neutron_fwaas/db/migration/alembic_migrations/versions/4202e3047e47_add_index_tenant_id.py neutron_fwaas/db/migration/alembic_migrations/versions/540142f314f4_fwaas_router_insertion.py neutron_fwaas/db/migration/alembic_migrations/versions/796c68dffbb_cisco_csr_fwaas.py neutron_fwaas/db/migration/alembic_migrations/versions/CONTRACT_HEAD neutron_fwaas/db/migration/alembic_migrations/versions/EXPAND_HEAD neutron_fwaas/db/migration/alembic_migrations/versions/kilo_release.py neutron_fwaas/db/migration/alembic_migrations/versions/start_neutron_fwaas.py neutron_fwaas/db/migration/alembic_migrations/versions/2023.2/expand/6941ce70131e_add_standard_attr_id.py neutron_fwaas/db/migration/alembic_migrations/versions/liberty/contract/67c8e8d61d5_initial.py neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/4b47ea298795_add_reject_rule.py neutron_fwaas/db/migration/alembic_migrations/versions/liberty/expand/c40fbb377ad_initial.py neutron_fwaas/db/migration/alembic_migrations/versions/mitaka/contract/458aa42b14b_fw_table_alter.py neutron_fwaas/db/migration/alembic_migrations/versions/newton/contract/f83a0b2964d0_rename_tenant_to_project.py neutron_fwaas/db/migration/alembic_migrations/versions/newton/expand/d6a12e637e28_neutron_fwaas_v2_0.py neutron_fwaas/db/migration/alembic_migrations/versions/pike/contract/fd38cd995cc0_shared_attribute_for_firewall_resources.py neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/876782258a43_create_default_firewall_groups_table.py neutron_fwaas/db/migration/alembic_migrations/versions/queens/expand/f24e0d5e5bff_uniq_firewallgroupportassociation0port.py neutron_fwaas/db/models/__init__.py neutron_fwaas/db/models/head.py neutron_fwaas/extensions/__init__.py neutron_fwaas/extensions/firewall_v2.py neutron_fwaas/extensions/firewall_v2_stdattrs.py neutron_fwaas/policies/__init__.py neutron_fwaas/policies/firewall_group.py neutron_fwaas/policies/firewall_policy.py neutron_fwaas/policies/firewall_rule.py neutron_fwaas/privileged/__init__.py neutron_fwaas/privileged/netlink_constants.py neutron_fwaas/privileged/netlink_lib.py neutron_fwaas/privileged/utils.py neutron_fwaas/privileged/netfilter_log/__init__.py neutron_fwaas/privileged/netfilter_log/libnetfilter_log.py neutron_fwaas/privileged/tests/__init__.py neutron_fwaas/privileged/tests/functional/__init__.py neutron_fwaas/privileged/tests/functional/dummy.py neutron_fwaas/privileged/tests/functional/utils.py neutron_fwaas/services/__init__.py neutron_fwaas/services/firewall/__init__.py neutron_fwaas/services/firewall/fwaas_plugin_v2.py neutron_fwaas/services/firewall/service_drivers/__init__.py neutron_fwaas/services/firewall/service_drivers/driver_api.py neutron_fwaas/services/firewall/service_drivers/agents/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/agents.py neutron_fwaas/services/firewall/service_drivers/agents/firewall_agent_api.py neutron_fwaas/services/firewall/service_drivers/agents/firewall_service.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/conntrack_base.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/fwaas_base_v2.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/iptables_fwaas_v2.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/legacy_conntrack.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/netlink_conntrack.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/driver_base.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/noop/noop_driver.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/constants.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/exceptions.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/firewall.py neutron_fwaas/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/rules.py neutron_fwaas/services/firewall/service_drivers/agents/l2/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/l2/fwaas_v2.py neutron_fwaas/services/firewall/service_drivers/agents/l3reference/__init__.py neutron_fwaas/services/firewall/service_drivers/agents/l3reference/firewall_l3_agent_v2.py neutron_fwaas/services/logapi/__init__.py neutron_fwaas/services/logapi/constants.py neutron_fwaas/services/logapi/exceptions.py neutron_fwaas/services/logapi/fwg_validate.py neutron_fwaas/services/logapi/agents/__init__.py neutron_fwaas/services/logapi/agents/drivers/__init__.py neutron_fwaas/services/logapi/agents/drivers/iptables/__init__.py neutron_fwaas/services/logapi/agents/drivers/iptables/driver.py neutron_fwaas/services/logapi/agents/drivers/iptables/log.py neutron_fwaas/services/logapi/agents/l3/__init__.py neutron_fwaas/services/logapi/agents/l3/fwg_log.py neutron_fwaas/services/logapi/common/__init__.py neutron_fwaas/services/logapi/common/fwg_callback.py neutron_fwaas/services/logapi/common/log_db_api.py neutron_fwaas/services/logapi/common/port_callback.py neutron_fwaas/services/logapi/rpc/__init__.py neutron_fwaas/services/logapi/rpc/log_server.py neutron_fwaas/tests/__init__.py neutron_fwaas/tests/base.py neutron_fwaas/tests/contrib/README neutron_fwaas/tests/contrib/filters.template neutron_fwaas/tests/contrib/functional-testing.filters neutron_fwaas/tests/contrib/gate_hook.sh neutron_fwaas/tests/contrib/gate_hook_tempest.sh neutron_fwaas/tests/contrib/post_test_hook.sh neutron_fwaas/tests/contrib/hooks/api_extensions-base neutron_fwaas/tests/contrib/hooks/api_extensions-legacy neutron_fwaas/tests/contrib/hooks/api_extensions-v1 neutron_fwaas/tests/contrib/hooks/api_extensions-v2 neutron_fwaas/tests/contrib/hooks/iptables_verify neutron_fwaas/tests/fullstack/README neutron_fwaas/tests/fullstack/__init__.py neutron_fwaas/tests/fullstack/base.py neutron_fwaas/tests/fullstack/test_l3_agent.py neutron_fwaas/tests/fullstack/utils.py neutron_fwaas/tests/fullstack/resources/__init__.py neutron_fwaas/tests/fullstack/resources/client.py neutron_fwaas/tests/fullstack/resources/config.py neutron_fwaas/tests/fullstack/resources/environment.py neutron_fwaas/tests/fullstack/resources/machine.py neutron_fwaas/tests/fullstack/resources/process.py neutron_fwaas/tests/functional/__init__.py neutron_fwaas/tests/functional/db/__init__.py neutron_fwaas/tests/functional/db/test_migrations.py neutron_fwaas/tests/functional/privileged/__init__.py neutron_fwaas/tests/functional/privileged/test_dummy.py neutron_fwaas/tests/functional/privileged/test_netlink_lib.py neutron_fwaas/tests/functional/privileged/test_utils.py neutron_fwaas/tests/functional/services/__init__.py neutron_fwaas/tests/functional/services/logapi/__init__.py neutron_fwaas/tests/functional/services/logapi/agents/__init__.py neutron_fwaas/tests/functional/services/logapi/agents/drivers/__init__.py neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/__init__.py neutron_fwaas/tests/functional/services/logapi/agents/drivers/iptables/test_log.py neutron_fwaas/tests/unit/__init__.py neutron_fwaas/tests/unit/cmd/__init__.py neutron_fwaas/tests/unit/cmd/upgrade_checks/__init__.py neutron_fwaas/tests/unit/cmd/upgrade_checks/test_checks.py neutron_fwaas/tests/unit/db/__init__.py neutron_fwaas/tests/unit/db/firewall/__init__.py neutron_fwaas/tests/unit/db/firewall/v2/__init__.py neutron_fwaas/tests/unit/db/firewall/v2/test_firewall_db_v2.py neutron_fwaas/tests/unit/privileged/__init__.py neutron_fwaas/tests/unit/privileged/test_netlink_lib.py neutron_fwaas/tests/unit/privileged/test_utils.py neutron_fwaas/tests/unit/privileged/netfilter_log/__init__.py neutron_fwaas/tests/unit/privileged/netfilter_log/test_libnetfilter_log.py neutron_fwaas/tests/unit/services/__init__.py neutron_fwaas/tests/unit/services/firewall/__init__.py neutron_fwaas/tests/unit/services/firewall/test_fwaas_plugin_v2.py neutron_fwaas/tests/unit/services/firewall/service_drivers/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/test_driver_api.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_agents.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_agent_api.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/test_firewall_service.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_iptables_fwaas_v2.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_legacy_conntrack.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/test_netlink_conntrack.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/noop/test_noop_driver.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_firewall.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/drivers/linux/l2/openvswitch_firewall/test_rules.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/fake_data.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l2/test_fwaas_v2.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/__init__.py neutron_fwaas/tests/unit/services/firewall/service_drivers/agents/l3reference/test_firewall_l3_agent_v2.py neutron_fwaas/tests/unit/services/logapi/__init__.py neutron_fwaas/tests/unit/services/logapi/base.py neutron_fwaas/tests/unit/services/logapi/test_fwg_validate.py neutron_fwaas/tests/unit/services/logapi/agents/__init__.py neutron_fwaas/tests/unit/services/logapi/agents/drivers/__init__.py neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/__init__.py neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/test_driver.py neutron_fwaas/tests/unit/services/logapi/agents/drivers/iptables/test_log.py neutron_fwaas/tests/unit/services/logapi/agents/l3/__init__.py neutron_fwaas/tests/unit/services/logapi/agents/l3/test_fwg_log.py neutron_fwaas/tests/unit/services/logapi/common/__init__.py neutron_fwaas/tests/unit/services/logapi/common/test_fwg_callback.py neutron_fwaas/tests/unit/services/logapi/common/test_log_db_api.py neutron_fwaas/tests/unit/services/logapi/common/test_port_callback.py neutron_fwaas/tests/unit/services/logapi/rpc/__init__.py neutron_fwaas/tests/unit/services/logapi/rpc/test_log_server.py playbooks/configure_functional_job.yaml releasenotes/notes/.placeholder releasenotes/notes/adding-new-tables-for-future-consumption-ffd537c1f82e2e01.yaml releasenotes/notes/auto-association-default-firewall-group-7e9faf1afca1df85.yaml releasenotes/notes/bug-1702242-c917c832ac2fa4e1.yaml releasenotes/notes/bug-1746404-493a66faac333403.yaml releasenotes/notes/bug-1799358-360c6ab27a32e0ac.yaml releasenotes/notes/cisco-fwaas-driver-move-8f46325d13c93543.yaml releasenotes/notes/coexistence-between-sg-and-fwg-1f77a755539a9463.yaml releasenotes/notes/config-file-generation-265c5256668a26bf.yaml releasenotes/notes/deprecate-neutron-fwaas-as-stadium-project-934d6acb3e824249.yaml releasenotes/notes/drop-python-2-7-73d3113c69d724c1.yaml releasenotes/notes/drop-python-3-6-and-3-7-b1cf8738aaab988f.yaml releasenotes/notes/enable-quotas-a3d0a21743bb1985.yaml releasenotes/notes/fwaas-config-9c780ccfb0e7887f.yaml releasenotes/notes/fwaas-v2-logging-79cbaa43ff17f47f.yaml releasenotes/notes/fwaas_v2-374471c215af0ca0.yaml releasenotes/notes/mcafee-fwaas-driver-removal-8915271e5d4288cf.yaml releasenotes/notes/ovs-firewall-driver-c347ea0a560b7e38.yaml releasenotes/notes/remove_fwaas_v1-15c6e19484f46d1b.yaml releasenotes/notes/validation_if_port_is_supported-639d0df705eb67f9.yaml releasenotes/notes/varmour-fwaas-driver-removal-f7aa304a4544134a.yaml releasenotes/notes/vyatta-fwaas-driver-removal-e38e6ecde5105084.yaml releasenotes/source/2023.1.rst releasenotes/source/2023.2.rst releasenotes/source/conf.py releasenotes/source/index.rst releasenotes/source/liberty.rst releasenotes/source/mitaka.rst releasenotes/source/newton.rst releasenotes/source/ocata.rst releasenotes/source/pike.rst releasenotes/source/queens.rst releasenotes/source/rocky.rst releasenotes/source/stein.rst releasenotes/source/unreleased.rst releasenotes/source/zed.rst releasenotes/source/_static/.placeholder releasenotes/source/_templates/.placeholder releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po roles/configure_functional_tests/README.rst roles/configure_functional_tests/defaults/main.yaml roles/configure_functional_tests/tasks/main.yaml tools/check_unit_test_structure.sh tools/clean.sh tools/configure_for_func_testing.sh tools/configure_for_fwaas_func_testing.sh tools/deploy_rootwrap.sh tools/generate_config_file_samples.sh././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/dependency_links.txt0000664000175000017500000000000100000000000025020 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/entry_points.txt0000664000175000017500000000346100000000000024254 0ustar00zuulzuul00000000000000[console_scripts] neutron-fwaas-migrate-v1-to-v2 = neutron_fwaas.cmd.v1_to_v2_db_migration:main [firewall_drivers] iptables_v2 = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.iptables_fwaas_v2:IptablesFwaasDriver [neutron.agent.l2.extensions] fwaas_v2 = neutron_fwaas.services.firewall.service_drivers.agents.l2.fwaas_v2:FWaaSV2AgentExtension [neutron.agent.l2.firewall_drivers] noop = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver ovs = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.openvswitch_firewall.firewall:OVSFirewallDriver [neutron.agent.l3.extensions] fwaas_v2 = neutron_fwaas.services.firewall.service_drivers.agents.l3reference.firewall_l3_agent_v2:L3WithFWaaS fwaas_v2_log = neutron_fwaas.services.logapi.agents.l3.fwg_log:FWaaSL3LoggingExtension [neutron.agent.l3.firewall_drivers] conntrack = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.legacy_conntrack:ConntrackLegacy netlink_conntrack = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.netlink_conntrack:ConntrackNetlink [neutron.db.alembic_migrations] neutron-fwaas = neutron_fwaas.db.migration:alembic_migrations [neutron.policies] neutron-fwaas = neutron_fwaas.policies:list_rules [neutron.service_plugins] firewall_v2 = neutron_fwaas.services.firewall.fwaas_plugin_v2:FirewallPluginV2 [neutron.services.logapi.drivers] fwaas_v2_log = neutron_fwaas.services.logapi.agents.drivers.iptables.log:IptablesLoggingDriver [neutron.status.upgrade.checks] neutron_fwaas = neutron_fwaas.cmd.upgrade_checks.checks:Checks [oslo.config.opts] firewall.agent = neutron_fwaas.opts:list_agent_opts neutron.fwaas = neutron_fwaas.opts:list_opts [oslo.policy.policies] neutron-fwaas = neutron_fwaas.policies:list_rules ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/not-zip-safe0000664000175000017500000000000100000000000023200 0ustar00zuulzuul00000000000000 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/pbr.json0000664000175000017500000000006000000000000022424 0ustar00zuulzuul00000000000000{"git_version": "293df0f63", "is_release": true}././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/requires.txt0000664000175000017500000000052500000000000023354 0ustar00zuulzuul00000000000000SQLAlchemy>=1.4.23 alembic>=1.6.5 eventlet!=0.18.3,!=0.20.1,>=0.18.2 netaddr>=0.7.18 neutron-lib>=3.6.1 neutron>=23.0.0.0b2 os-ken>=0.3.0 oslo.config>=5.2.0 oslo.db>=4.37.0 oslo.log>=3.36.0 oslo.messaging>=5.29.0 oslo.privsep>=1.32.0 oslo.service!=1.28.1,>=1.24.0 oslo.utils>=3.33.0 pyzmq>=14.3.1 [:(sys_platform!='win32')] pyroute2>=0.7.2 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145114.0 neutron-fwaas-20.0.0/neutron_fwaas.egg-info/top_level.txt0000664000175000017500000000001600000000000023501 0ustar00zuulzuul00000000000000neutron_fwaas ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3293517 neutron-fwaas-20.0.0/playbooks/0000775000175000017500000000000000000000000016410 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/playbooks/configure_functional_job.yaml0000664000175000017500000000011200000000000024323 0ustar00zuulzuul00000000000000- hosts: all roles: - setup_logdir - configure_functional_tests ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2773497 neutron-fwaas-20.0.0/releasenotes/0000775000175000017500000000000000000000000017076 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/releasenotes/notes/0000775000175000017500000000000000000000000020226 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/.placeholder0000664000175000017500000000000000000000000022477 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000020600000000000011453 xustar0000000000000000112 path=neutron-fwaas-20.0.0/releasenotes/notes/adding-new-tables-for-future-consumption-ffd537c1f82e2e01.yaml 22 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/adding-new-tables-for-future-consumption-ffd537c1f82e2e01.ya0000664000175000017500000000065500000000000033021 0ustar00zuulzuul00000000000000--- prelude: > Adding new tables for future consumption. features: - | New tables ``ACCEPTED_EGRESS_TRAFFIC_TABLE=91`` and ``ACCEPTED_INGRESS_TRAFFIC_TABLE=92`` & ``DROPPED_TRAFFIC_TABLE=93`` are added to OVS based FWaaS L2 driver for future comsumption like logging service. fixes: - | The limitation related to logging for security group in case of co-existence between SG and FWG is also fixed.././@PaxHeader0000000000000000000000000000020500000000000011452 xustar0000000000000000111 path=neutron-fwaas-20.0.0/releasenotes/notes/auto-association-default-firewall-group-7e9faf1afca1df85.yaml 22 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/auto-association-default-firewall-group-7e9faf1afca1df85.yam0000664000175000017500000000071000000000000033320 0ustar00zuulzuul00000000000000--- prelude: > Associating default firewall group for new VM ports within a project automatically. features: - | The default firewall group won't be applied to all new VM ports as default. However, if option ``auto_associate_default_firewall_group`` is enabled in neutron_fwaas.conf like: [fwaas] auto_associate_default_firewall_group = True Then, the default firewall group will be applied to all new VM ports. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/bug-1702242-c917c832ac2fa4e1.yaml0000664000175000017500000000076200000000000025003 0ustar00zuulzuul00000000000000--- fixes: - | [`bug 1702242 `__] Port range specification of a firewall rule now works expectedly with the reference L3 agent based implementation. Previously, when creating a firewall rule with port range like ``8778:9000``, the rule was not deleted correctly and only entries associated with the first port number were clean up. Note that this bug is only applied to the reference L3 agent based implementation. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/bug-1746404-493a66faac333403.yaml0000664000175000017500000000067700000000000024655 0ustar00zuulzuul00000000000000--- prelude: > Taking security for VM instance into consideration, we've removed an option to disable automatic association with default firewall group feature. Therefore, `auto_associate_default_firewall_group` has been removed. fixes: - | There is no validation to check if an updated port is for VM or not so far. After this fix, default firewall group association is called only for VM ports which are newly created. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/bug-1799358-360c6ab27a32e0ac.yaml0000664000175000017500000000040400000000000025006 0ustar00zuulzuul00000000000000--- fixes: - | There was no way to define default firewall group rules. Default firewall group rules can be now defined in neutron_fwaas.conf in section ``default_fwg_rules``. Default firewall group rules are same as hardcoded values before. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/cisco-fwaas-driver-move-8f46325d13c93543.yaml0000664000175000017500000000053300000000000027551 0ustar00zuulzuul00000000000000--- prelude: > The Cisco Firewall Driver is being moved from the FWaaS repo to the Cisco specific repo: https://github.com/openstack/networking-cisco upgrade: - The Cisco FWaaS driver will not be available from the neutron-fwaas repo in Newton. For the Cisco FWaaS driver, refer to the openstack/networking-cisco repo. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/coexistence-between-sg-and-fwg-1f77a755539a9463.yaml0000664000175000017500000000141200000000000031014 0ustar00zuulzuul00000000000000--- prelude: > Coexistence between security group and firewall group. features: - L2 firewall group driver based OVS can work in coexistence mode. That means, if a port is associated with both firewall group and security group, then a packet must be allowed by both features. other: - If a port is associated with both firewall group & security group and there is a security group logging, which is enabled to collect ``DROP`` events for this port, then most of invalid packets will be dropped at firewall group for performance reason except first dropped packet, which is allowed by firewall group but not accepted by security group. So not every dropped packet will be logged (like in case of security group works in standalone mode). ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/config-file-generation-265c5256668a26bf.yaml0000664000175000017500000000043500000000000027513 0ustar00zuulzuul00000000000000--- prelude: > Generation of sample Neutron FWaaS configuration files. features: - Neutron FWaaS no longer includes static example configuration files. Instead, use tools/generate_config_file_samples.sh to generate them. The files are generated with a .sample extension. ././@PaxHeader0000000000000000000000000000021000000000000011446 xustar0000000000000000114 path=neutron-fwaas-20.0.0/releasenotes/notes/deprecate-neutron-fwaas-as-stadium-project-934d6acb3e824249.yaml 22 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/deprecate-neutron-fwaas-as-stadium-project-934d6acb3e824249.0000664000175000017500000000114500000000000032626 0ustar00zuulzuul00000000000000--- prelude: > Neutron-fwaas project is now deprecated in the Neutron stadium. deprecations: - | Due to lack of maintainers neutron-fwaas project is now deprecated in the Neutron stadium. There is no planned releases of this project in the ``Victoria`` cycle. In ``W`` cycle project will be moved out from the stadium to the unofficial OpenStack projects. If You want to step in and be maintainer of this project to keep it in the Neutron stadium, please contact the ``neutron team`` via openstack-discuss@lists.openstack.org or IRC channel #openstack-neutron @freenode. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/drop-python-2-7-73d3113c69d724c1.yaml0000664000175000017500000000021400000000000025747 0ustar00zuulzuul00000000000000--- upgrade: - | Python 2.7 support has been dropped. The minimum version of Python now supported by neutron-fwaas is Python 3.6. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/drop-python-3-6-and-3-7-b1cf8738aaab988f.yaml0000664000175000017500000000020100000000000027324 0ustar00zuulzuul00000000000000--- upgrade: - | Python 3.6 & 3.7 support has been dropped. The minimum version of Python now supported is Python 3.8. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/enable-quotas-a3d0a21743bb1985.yaml0000664000175000017500000000135200000000000025775 0ustar00zuulzuul00000000000000--- prelude: > Enable quotas for FWaaS. features: - The FWaaS extension will register quotas. The default values for quota_firewall and quota_firewall_policy are set to 10. The default value for quota_firewall_rule is set to 100. Quotas can be adjusted in the conf files, including -1 values to allow unlimited. issues: - Tenants may receive a 409 Conflict error with a message body containing a quota exceeded message during resource creation if their quota is exceeded. other: - Operators that increase the default limit for quota_routers from 10 may want to bump FWaaS quotas as well, since with router insertion a tenant can potentially have a unique policy and firewall for each router. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/fwaas-config-9c780ccfb0e7887f.yaml0000664000175000017500000000020500000000000025772 0ustar00zuulzuul00000000000000--- features: - Neutron Firewall as a Service can be configured by the users with the newly introduced fwaas configuration file. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/fwaas-v2-logging-79cbaa43ff17f47f.yaml0000664000175000017500000000157700000000000026564 0ustar00zuulzuul00000000000000--- prelude: > Resource type **firewall group** has been supported for neutron packet logging framework. You can specify firewall group as ``--resource-type`` for logging API. features: - | Enable to collect network packet log for ACCEPT/DROP action from firewall groups. Currently, packet logging supports only L3(router) ports. issues: - | [`bug 1720727 `__] Currently, we cannot specify the following combination on CLI due to missing validation of --resource-type: - --resource-type firewall_group --resource - --resource-type firewall_group --resource --target Therefore, you can only run with following combinations: - --resource-type firewall_group --target - --resource-type firewall_group ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/fwaas_v2-374471c215af0ca0.yaml0000664000175000017500000000167000000000000024743 0ustar00zuulzuul00000000000000--- prelude: > The FWaaS team is pleased to release FWaaS v2.0. This release of FWaaS supports either the original FWaaS v1 or the new FWaaS v2. features: - In FWaaS v2 firewall policies are applied to router ports, as opposed to applying to routers in FWaaS v1. - Earlier the FWaaS agent integrated with the L3 agent by having the L3 Agent class inherit from the FWaaS Agent class. This meant that other service agents could not also integrate with the L3 agent. Now, using the L3 agent extensions mechanism, FWaaS (v1 and v2) plugs in to the L3 agent. This means that it can interoperate peacefully with other L3 advanced services that also implement the L3 agent extension mechanism, all without any code changes to Neutron. upgrade: - There is not currently a defined upgrade path from FWaaS v1 to FWaaS v2. - FWaaS v1 can not be enabled at the same time as FWaaS v2; one or the other must be chosen. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/mcafee-fwaas-driver-removal-8915271e5d4288cf.yaml0000664000175000017500000000033400000000000030460 0ustar00zuulzuul00000000000000--- prelude: > - The McAfee Firewall Driver is being removed from the FwaaS repo, due to lack of active maintainers. upgrade: - The McAfee Firewall Driver will not be available for use in the Newton release. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/ovs-firewall-driver-c347ea0a560b7e38.yaml0000664000175000017500000000103400000000000027224 0ustar00zuulzuul00000000000000--- issues: - | Currently, the FWaaSv2 L2 driver can be configured as: ``firewall_driver = ovs`` And the Security Group driver is specified as: ``firewall_driver = openvswitch`` If both are configured, the packet will still only hit the FWaaS table in OVS and will not traverse the rules in the SG table. There are some fixes needed to support this model which are being tested and will be merged shortly. Currently there are no checks to allow only one of FWaaS L2 or SG to be configured. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/remove_fwaas_v1-15c6e19484f46d1b.yaml0000664000175000017500000000050200000000000026335 0ustar00zuulzuul00000000000000--- prelude: > - FWaaS V1 is being removed from the neutron-fwaas repo. Because FWaaS V2 has been available since the Newton release. upgrade: - The FWaaS V1 source code will not be available in neutron-fwaas repo from Stein. neutron-fwaas-migrate-v1-to-v2 can be used for migrating V1 object to V2 model. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/validation_if_port_is_supported-639d0df705eb67f9.yaml0000664000175000017500000000044300000000000032023 0ustar00zuulzuul00000000000000--- prelude: > Validating if a port is supported by FWaaS V2 fixes: - | [`bug 1746855 `__] Now, FWaaS V2 will validate if a port is supported before adding it to a FWG. This helps to make sure FWaaS V2 API works as expected.././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/varmour-fwaas-driver-removal-f7aa304a4544134a.yaml0000664000175000017500000000037500000000000030763 0ustar00zuulzuul00000000000000--- prelude: > - The vArmour Firewall Driver is being removed from the FwaaS repo, as per decision to remove vendor drivers from the community repo. upgrade: - The vArmour Firewall Driver will not be available for use in the Newton release. ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/notes/vyatta-fwaas-driver-removal-e38e6ecde5105084.yaml0000664000175000017500000000042200000000000030673 0ustar00zuulzuul00000000000000--- prelude: > - The vyatta Firewall Driver is being removed from the FwaaS repo, as per decision to remove vendor drivers from the community repo. upgrade: - The vyatta Firewall Driver will not be available for use in the Newton release from the community repo. ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/releasenotes/source/0000775000175000017500000000000000000000000020376 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/2023.1.rst0000664000175000017500000000020200000000000021647 0ustar00zuulzuul00000000000000=========================== 2023.1 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.1 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/2023.2.rst0000664000175000017500000000020200000000000021650 0ustar00zuulzuul00000000000000=========================== 2023.2 Series Release Notes =========================== .. release-notes:: :branch: stable/2023.2 ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/releasenotes/source/_static/0000775000175000017500000000000000000000000022024 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/_static/.placeholder0000664000175000017500000000000000000000000024275 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/releasenotes/source/_templates/0000775000175000017500000000000000000000000022533 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/_templates/.placeholder0000664000175000017500000000000000000000000025004 0ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/conf.py0000664000175000017500000002176300000000000021706 0ustar00zuulzuul00000000000000# -*- coding: utf-8 -*- # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT 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 FWaaS 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', ] # 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 FWaaS Release Notes' copyright = '2015, Neutron FWaaS Developers' # Release notes are version independent. # The full version, including alpha/beta/rc tags. release = '' # The short X.Y version. version = '' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'openstackdocs' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' html_last_updated_fmt = '%Y-%m-%d %H:%M' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_domain_indices = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'NeutronFWaaSReleaseNotesdoc' # -- 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', 'NeutronFWaaSReleaseNotes.tex', 'Neutron FWaaS Release Notes Documentation', 'Neutron FWaaS 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', 'neutronfwaasreleasenotes', 'Neutron FWaaS Release Notes ' 'Documentation', ['Neutron FWaaS 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', 'NeutronFWaaSReleaseNotes', 'Neutron FWaaS Release Notes ' 'Documentation', 'Neutron FWaaS Developers', 'NeutronFWaaSReleaseNotes', '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/'] # -- Options for openstackdocstheme ------------------------------------------- repository_name = 'openstack/neutron-fwaas' bug_project = 'neutron' bug_tag = 'doc' ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/index.rst0000664000175000017500000000035500000000000022242 0ustar00zuulzuul00000000000000============================= Neutron FWaaS Release Notes ============================= .. toctree:: :maxdepth: 1 unreleased 2023.2 2023.1 zed stein rocky queens pike ocata newton mitaka liberty ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/liberty.rst0000664000175000017500000000022200000000000022576 0ustar00zuulzuul00000000000000============================== Liberty Series Release Notes ============================== .. release-notes:: :branch: origin/stable/liberty ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2773497 neutron-fwaas-20.0.0/releasenotes/source/locale/0000775000175000017500000000000000000000000021635 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2773497 neutron-fwaas-20.0.0/releasenotes/source/locale/en_GB/0000775000175000017500000000000000000000000022607 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/0000775000175000017500000000000000000000000024374 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/locale/en_GB/LC_MESSAGES/releasenotes.po0000664000175000017500000004277500000000000027444 0ustar00zuulzuul00000000000000# Andi Chandler , 2017. #zanata # Andi Chandler , 2018. #zanata # Andi Chandler , 2020. #zanata msgid "" msgstr "" "Project-Id-Version: neutron-fwaas\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2020-01-24 00:17+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2020-04-16 12:40+0000\n" "Last-Translator: Andi Chandler \n" "Language-Team: English (United Kingdom)\n" "Language: en_GB\n" "X-Generator: Zanata 4.3.3\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" msgid "--resource-type firewall_group" msgstr "--resource-type firewall_group" msgid "--resource-type firewall_group --resource " msgstr "--resource-type firewall_group --resource " msgid "" "--resource-type firewall_group --resource --target " "" msgstr "" "--resource-type firewall_group --resource --target " "" msgid "--resource-type firewall_group --target " msgstr "--resource-type firewall_group --target " msgid "11.0.0" msgstr "11.0.0" msgid "12.0.0" msgstr "12.0.0" msgid "13.0.0" msgstr "13.0.0" msgid "14.0.0" msgstr "14.0.0" msgid "15.0.0-9" msgstr "15.0.0-9" msgid "7.0.2" msgstr "7.0.2" msgid "7.1.1" msgstr "7.1.1" msgid "8.0.0" msgstr "8.0.0" msgid "9.0.0" msgstr "9.0.0" msgid "9.0.0.0b2" msgstr "9.0.0.0b2" msgid "9.0.0.0b3" msgstr "9.0.0.0b3" msgid "9.0.0.0rc1" msgstr "9.0.0.0rc1" msgid "Adding new tables for future consumption." msgstr "Adding new tables for future consumption." msgid "And the Security Group driver is specified as:" msgstr "And the Security Group driver is specified as:" msgid "" "Associating default firewall group for new VM ports within a project " "automatically." msgstr "" "Associating default firewall group for new VM ports within a project " "automatically." msgid "Bug Fixes" msgstr "Bug Fixes" msgid "Coexistence between security group and firewall group." msgstr "Coexistence between security group and firewall group." msgid "Current Series Release Notes" msgstr "Current Series Release Notes" msgid "Currently, the FWaaSv2 L2 driver can be configured as:" msgstr "Currently, the FWaaSv2 L2 driver can be configured as:" msgid "" "Earlier the FWaaS agent integrated with the L3 agent by having the L3 Agent " "class inherit from the FWaaS Agent class. This meant that other service " "agents could not also integrate with the L3 agent. Now, using the L3 agent " "extensions mechanism, FWaaS (v1 and v2) plugs in to the L3 agent. This " "means that it can interoperate peacefully with other L3 advanced services " "that also implement the L3 agent extension mechanism, all without any code " "changes to Neutron." msgstr "" "Earlier the FWaaS agent integrated with the L3 agent by having the L3 Agent " "class inherit from the FWaaS Agent class. This meant that other service " "agents could not also integrate with the L3 agent. Now, using the L3 agent " "extensions mechanism, FWaaS (v1 and v2) plugs in to the L3 agent. This " "means that it can interoperate peacefully with other L3 advanced services " "that also implement the L3 agent extension mechanism, all without any code " "changes to Neutron." msgid "Enable quotas for FWaaS." msgstr "Enable quotas for FWaaS." msgid "" "Enable to collect network packet log for ACCEPT/DROP action from firewall " "groups. Currently, packet logging supports only L3(router) ports." msgstr "" "Enable to collect network packet log for ACCEPT/DROP action from firewall " "groups. Currently, packet logging supports only L3(router) ports." msgid "" "FWaaS V1 is being removed from the neutron-fwaas repo. Because FWaaS V2 has " "been available since the Newton release." msgstr "" "FWaaS V1 is being removed from the neutron-fwaas repo. Because FWaaS V2 has " "been available since the Newton release." msgid "" "FWaaS v1 can not be enabled at the same time as FWaaS v2; one or the other " "must be chosen." msgstr "" "FWaaS v1 can not be enabled at the same time as FWaaS v2; one or the other " "must be chosen." msgid "Generation of sample Neutron FWaaS configuration files." msgstr "Generation of sample Neutron FWaaS configuration files." msgid "" "If a port is associated with both firewall group & security group and there " "is a security group logging, which is enabled to collect ``DROP`` events for " "this port, then most of invalid packets will be dropped at firewall group " "for performance reason except first dropped packet, which is allowed by " "firewall group but not accepted by security group. So not every dropped " "packet will be logged (like in case of security group works in standalone " "mode)." msgstr "" "If a port is associated with both firewall group & security group and there " "is a security group logging, which is enabled to collect ``DROP`` events for " "this port, then most of invalid packets will be dropped at firewall group " "for performance reason except first dropped packet, which is allowed by " "firewall group but not accepted by security group. So not every dropped " "packet will be logged (like in case of security group works in standalone " "mode)." msgid "" "If both are configured, the packet will still only hit the FWaaS table in " "OVS and will not traverse the rules in the SG table. There are some fixes " "needed to support this model which are being tested and will be merged " "shortly. Currently there are no checks to allow only one of FWaaS L2 or SG " "to be configured." msgstr "" "If both are configured, the packet will still only hit the FWaaS table in " "OVS and will not traverse the rules in the SG table. There are some fixes " "needed to support this model which are being tested and will be merged " "shortly. Currently there are no checks to allow only one of FWaaS L2 or SG " "to be configured." msgid "" "In FWaaS v2 firewall policies are applied to router ports, as opposed to " "applying to routers in FWaaS v1." msgstr "" "In FWaaS v2 firewall policies are applied to router ports, as opposed to " "applying to routers in FWaaS v1." msgid "Known Issues" msgstr "Known Issues" msgid "" "L2 firewall group driver based OVS can work in coexistence mode. That means, " "if a port is associated with both firewall group and security group, then a " "packet must be allowed by both features." msgstr "" "L2 firewall group driver based OVS can work in coexistence mode. That means, " "if a port is associated with both firewall group and security group, then a " "packet must be allowed by both features." msgid "Liberty Series Release Notes" msgstr "Liberty Series Release Notes" msgid "Mitaka Series Release Notes" msgstr "Mitaka Series Release Notes" msgid "Neutron FWaaS Release Notes" msgstr "Neutron FWaaS Release Notes" msgid "" "Neutron FWaaS no longer includes static example configuration files. " "Instead, use tools/generate_config_file_samples.sh to generate them. The " "files are generated with a .sample extension." msgstr "" "Neutron FWaaS no longer includes static example configuration files. " "Instead, use tools/generate_config_file_samples.sh to generate them. The " "files are generated with a .sample extension." msgid "" "Neutron Firewall as a Service can be configured by the users with the newly " "introduced fwaas configuration file." msgstr "" "Neutron Firewall as a Service can be configured by the users with the newly " "introduced FWaaS configuration file." msgid "New Features" msgstr "New Features" msgid "" "New tables ``ACCEPTED_EGRESS_TRAFFIC_TABLE=91`` and " "``ACCEPTED_INGRESS_TRAFFIC_TABLE=92`` & ``DROPPED_TRAFFIC_TABLE=93`` are " "added to OVS based FWaaS L2 driver for future comsumption like logging " "service." msgstr "" "New tables ``ACCEPTED_EGRESS_TRAFFIC_TABLE=91`` and " "``ACCEPTED_INGRESS_TRAFFIC_TABLE=92`` & ``DROPPED_TRAFFIC_TABLE=93`` are " "added to OVS based FWaaS L2 driver for future consumption like logging " "service." msgid "Newton Series Release Notes" msgstr "Newton Series Release Notes" msgid "Ocata Series Release Notes" msgstr "Ocata Series Release Notes" msgid "" "Operators that increase the default limit for quota_routers from 10 may want " "to bump FWaaS quotas as well, since with router insertion a tenant can " "potentially have a unique policy and firewall for each router." msgstr "" "Operators that increase the default limit for quota_routers from 10 may want " "to bump FWaaS quotas as well, since with router insertion a tenant can " "potentially have a unique policy and firewall for each router." msgid "Other Notes" msgstr "Other Notes" msgid "Pike Series Release Notes" msgstr "Pike Series Release Notes" msgid "Prelude" msgstr "Prelude" msgid "" "Python 2.7 support has been dropped. The minimum version of Python now " "supported by neutron-fwaas is Python 3.6." msgstr "" "Python 2.7 support has been dropped. The minimum version of Python now " "supported by neutron-fwaas is Python 3.6." msgid "Queens Series Release Notes" msgstr "Queens Series Release Notes" msgid "" "Resource type **firewall group** has been supported for neutron packet " "logging framework. You can specify firewall group as ``--resource-type`` " "for logging API." msgstr "" "Resource type **firewall group** has been supported for neutron packet " "logging framework. You can specify firewall group as ``--resource-type`` " "for logging API." msgid "Rocky Series Release Notes" msgstr "Rocky Series Release Notes" msgid "Start using reno to manage release notes." msgstr "Start using Reno to manage release notes." msgid "Stein Series Release Notes" msgstr "Stein Series Release Notes" msgid "" "Taking security for VM instance into consideration, we've removed an option " "to disable automatic association with default firewall group feature. " "Therefore, `auto_associate_default_firewall_group` has been removed." msgstr "" "Taking security for VM instance into consideration, we've removed an option " "to disable automatic association with default firewall group feature. " "Therefore, `auto_associate_default_firewall_group` has been removed." msgid "" "Tenants may receive a 409 Conflict error with a message body containing a " "quota exceeded message during resource creation if their quota is exceeded." msgstr "" "Tenants may receive a 409 Conflict error with a message body containing a " "quota exceeded message during resource creation if their quota is exceeded." msgid "" "The Cisco FWaaS driver will not be available from the neutron-fwaas repo in " "Newton. For the Cisco FWaaS driver, refer to the openstack/networking-cisco " "repo." msgstr "" "The Cisco FWaaS driver will not be available from the neutron-fwaas repo in " "Newton. For the Cisco FWaaS driver, refer to the openstack/networking-cisco " "repo." msgid "" "The Cisco Firewall Driver is being moved from the FWaaS repo to the Cisco " "specific repo: https://github.com/openstack/networking-cisco" msgstr "" "The Cisco Firewall Driver is being moved from the FWaaS repo to the Cisco " "specific repo: https://github.com/openstack/networking-cisco" msgid "" "The FWaaS V1 source code will not be available in neutron-fwaas repo from " "Stein. neutron-fwaas-migrate-v1-to-v2 can be used for migrating V1 object to " "V2 model." msgstr "" "The FWaaS V1 source code will not be available in neutron-fwaas repo from " "Stein. neutron-fwaas-migrate-v1-to-v2 can be used for migrating V1 object to " "V2 model." msgid "" "The FWaaS extension can register quotas. The default values for " "quota_firewall, quota_firewall_policy, and quota_firewall_rule are set to -1 " "(unlimited)." msgstr "" "The FWaaS extension can register quotas. The default values for " "quota_firewall, quota_firewall_policy, and quota_firewall_rule are set to -1 " "(unlimited)." msgid "" "The FWaaS extension will register quotas. The default values for " "quota_firewall and quota_firewall_policy are set to 10. The default value " "for quota_firewall_rule is set to 100. Quotas can be adjusted in the conf " "files, including -1 values to allow unlimited." msgstr "" "The FWaaS extension will register quotas. The default values for " "quota_firewall and quota_firewall_policy are set to 10. The default value " "for quota_firewall_rule is set to 100. Quotas can be adjusted in the conf " "files, including -1 values to allow unlimited." msgid "" "The FWaaS team is pleased to release FWaaS v2.0. This release of FWaaS " "supports either the original FWaaS v1 or the new FWaaS v2." msgstr "" "The FWaaS team is pleased to release FWaaS v2.0. This release of FWaaS " "supports either the original FWaaS v1 or the new FWaaS v2." msgid "" "The McAfee Firewall Driver is being removed from the FwaaS repo, due to lack " "of active maintainers." msgstr "" "The McAfee Firewall Driver is being removed from the FWaaS repo, due to lack " "of active maintainers." msgid "" "The McAfee Firewall Driver will not be available for use in the Newton " "release." msgstr "" "The McAfee Firewall Driver will not be available for use in the Newton " "release." msgid "" "The default firewall group won't be applied to all new VM ports as default. " "However, if option ``auto_associate_default_firewall_group`` is enabled in " "neutron_fwaas.conf like:" msgstr "" "The default firewall group won't be applied to all new VM ports as default. " "However, if option ``auto_associate_default_firewall_group`` is enabled in " "neutron_fwaas.conf like:" msgid "" "The limitation related to logging for security group in case of co-existence " "between SG and FWG is also fixed." msgstr "" "The limitation related to logging for security group in case of co-existence " "between SG and FWG is also fixed." msgid "" "The vArmour Firewall Driver is being removed from the FwaaS repo, as per " "decision to remove vendor drivers from the community repo." msgstr "" "The vArmour Firewall Driver is being removed from the FWaaS repo, as per " "decision to remove vendor drivers from the community repo." msgid "" "The vArmour Firewall Driver will not be available for use in the Newton " "release." msgstr "" "The vArmour Firewall Driver will not be available for use in the Newton " "release." msgid "The vyatta Firewall Driver is being removed from the FwaaS repo," msgstr "The Vyatta Firewall Driver is being removed from the FWaaS repo," msgid "" "The vyatta Firewall Driver will not be available for use in the Newton " "release from the community repo." msgstr "" "The Vyatta Firewall Driver will not be available for use in the Newton " "release from the community repo." msgid "Then, the default firewall group will be applied to all new VM ports." msgstr "Then, the default firewall group will be applied to all new VM ports." msgid "" "There is no validation to check if an updated port is for VM or not so far. " "After this fix, default firewall group association is called only for VM " "ports which are newly created." msgstr "" "There is no validation to check if an updated port is for VM or not so far. " "After this fix, default firewall group association is called only for VM " "ports which are newly created." msgid "" "There is not currently a defined upgrade path from FWaaS v1 to FWaaS v2." msgstr "" "There is not currently a defined upgrade path from FWaaS v1 to FWaaS v2." msgid "Therefore, you can only run with following combinations:" msgstr "Therefore, you can only run with following combinations:" msgid "Upgrade Notes" msgstr "Upgrade Notes" msgid "Validating if a port is supported by FWaaS V2" msgstr "Validating if a port is supported by FWaaS V2" msgid "" "[`bug 1702242 `__] Port " "range specification of a firewall rule now works expectedly with the " "reference L3 agent based implementation. Previously, when creating a " "firewall rule with port range like ``8778:9000``, the rule was not deleted " "correctly and only entries associated with the first port number were clean " "up. Note that this bug is only applied to the reference L3 agent based " "implementation." msgstr "" "[`bug 1702242 `__] Port " "range specification of a firewall rule now works expectedly with the " "reference L3 agent based implementation. Previously, when creating a " "firewall rule with port range like ``8778:9000``, the rule was not deleted " "correctly and only entries associated with the first port number were clean " "up. Note that this bug is only applied to the reference L3 agent based " "implementation." msgid "" "[`bug 1720727 `__] " "Currently, we cannot specify the following combination on CLI due to missing " "validation of --resource-type:" msgstr "" "[`bug 1720727 `__] " "Currently, we cannot specify the following combination on CLI due to missing " "validation of --resource-type:" msgid "" "[`bug 1746855 `__] Now, " "FWaaS V2 will validate if a port is supported before adding it to a FWG. " "This helps to make sure FWaaS V2 API works as expected." msgstr "" "[`bug 1746855 `__] Now, " "FWaaS V2 will validate if a port is supported before adding it to a FWG. " "This helps to make sure FWaaS V2 API works as expected." msgid "[fwaas] auto_associate_default_firewall_group = True" msgstr "[fwaas] auto_associate_default_firewall_group = True" msgid "``firewall_driver = openvswitch``" msgstr "``firewall_driver = openvswitch``" msgid "``firewall_driver = ovs``" msgstr "``firewall_driver = ovs``" msgid "as per decision to remove vendor drivers from the community repo." msgstr "as per decision to remove vendor drivers from the community repo." ././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.2773497 neutron-fwaas-20.0.0/releasenotes/source/locale/fr/0000775000175000017500000000000000000000000022244 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/releasenotes/source/locale/fr/LC_MESSAGES/0000775000175000017500000000000000000000000024031 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/locale/fr/LC_MESSAGES/releasenotes.po0000664000175000017500000000267300000000000027072 0ustar00zuulzuul00000000000000# Gérald LONLAS , 2016. #zanata msgid "" msgstr "" "Project-Id-Version: Neutron FWaaS Release Notes 11.0.0\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-08-16 20:31+0000\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "PO-Revision-Date: 2016-10-22 05:48+0000\n" "Last-Translator: Gérald LONLAS \n" "Language-Team: French\n" "Language: fr\n" "X-Generator: Zanata 3.9.6\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" msgid "7.0.2" msgstr "7.0.2" msgid "7.1.1" msgstr "7.1.1" msgid "8.0.0" msgstr "8.0.0" msgid "9.0.0" msgstr "9.0.0" msgid "9.0.0.0b2" msgstr "9.0.0.0b2" msgid "9.0.0.0b3" msgstr "9.0.0.0b3" msgid "9.0.0.0rc1" msgstr "9.0.0.0rc1" msgid "Current Series Release Notes" msgstr "Note de la release actuelle" msgid "Known Issues" msgstr "Problèmes connus" msgid "Liberty Series Release Notes" msgstr "Note de release pour Liberty" msgid "Mitaka Series Release Notes" msgstr "Note de release pour Mitaka" msgid "Neutron FWaaS Release Notes" msgstr "Note de release de Neutron FWaaS" msgid "New Features" msgstr "Nouvelles fonctionnalités" msgid "Newton Series Release Notes" msgstr "Note de release pour Newton" msgid "Other Notes" msgstr "Autres notes" msgid "Start using reno to manage release notes." msgstr "Commence à utiliser reno pour la gestion des notes de release" msgid "Upgrade Notes" msgstr "Notes de mises à jours" ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/mitaka.rst0000664000175000017500000000023200000000000022373 0ustar00zuulzuul00000000000000=================================== Mitaka Series Release Notes =================================== .. release-notes:: :branch: origin/stable/mitaka ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/newton.rst0000664000175000017500000000023200000000000022437 0ustar00zuulzuul00000000000000=================================== Newton Series Release Notes =================================== .. release-notes:: :branch: origin/stable/newton ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/ocata.rst0000664000175000017500000000023000000000000022212 0ustar00zuulzuul00000000000000=================================== Ocata Series Release Notes =================================== .. release-notes:: :branch: origin/stable/ocata ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/pike.rst0000664000175000017500000000021700000000000022060 0ustar00zuulzuul00000000000000=================================== Pike Series Release Notes =================================== .. release-notes:: :branch: stable/pike ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/queens.rst0000664000175000017500000000022300000000000022425 0ustar00zuulzuul00000000000000=================================== Queens Series Release Notes =================================== .. release-notes:: :branch: stable/queens ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/rocky.rst0000664000175000017500000000022100000000000022252 0ustar00zuulzuul00000000000000=================================== Rocky Series Release Notes =================================== .. release-notes:: :branch: stable/rocky ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/stein.rst0000664000175000017500000000022100000000000022245 0ustar00zuulzuul00000000000000=================================== Stein Series Release Notes =================================== .. release-notes:: :branch: stable/stein ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/unreleased.rst0000664000175000017500000000016000000000000023254 0ustar00zuulzuul00000000000000============================== Current Series Release Notes ============================== .. release-notes:: ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/releasenotes/source/zed.rst0000664000175000017500000000016600000000000021715 0ustar00zuulzuul00000000000000======================== Zed Series Release Notes ======================== .. release-notes:: :branch: stable/zed ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/requirements.txt0000664000175000017500000000151600000000000017674 0ustar00zuulzuul00000000000000eventlet!=0.18.3,!=0.20.1,>=0.18.2 # MIT netaddr>=0.7.18 # BSD SQLAlchemy>=1.4.23 # MIT alembic>=1.6.5 # MIT neutron-lib>=3.6.1 # Apache-2.0 os-ken >= 0.3.0 # Apache-2.0 oslo.config>=5.2.0 # Apache-2.0 oslo.db>=4.37.0 # Apache-2.0 oslo.log>=3.36.0 # Apache-2.0 oslo.messaging>=5.29.0 # Apache-2.0 oslo.service!=1.28.1,>=1.24.0 # Apache-2.0 oslo.utils>=3.33.0 # Apache-2.0 oslo.privsep>=1.32.0 # Apache-2.0 pyroute2>=0.7.2;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2) neutron>=23.0.0.0b2 # Apache-2.0 pyzmq>=14.3.1 # LGPL+BSD # 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=1712145114.2773497 neutron-fwaas-20.0.0/roles/0000775000175000017500000000000000000000000015531 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000003400000000000011452 xustar000000000000000028 mtime=1712145114.3373518 neutron-fwaas-20.0.0/roles/configure_functional_tests/0000775000175000017500000000000000000000000023156 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/roles/configure_functional_tests/README.rst0000664000175000017500000000110700000000000024644 0ustar00zuulzuul00000000000000Configure host to run on it Neutron functional/fullstack tests **Role Variables** .. zuul:rolevar:: tests_venv :default: {{ tox_envlist }} .. zuul:rolevar:: project_name :default: neutron .. zuul:rolevar:: base_dir :default: {{ ansible_user_dir }}/src/opendev.org .. zuul:rolevar:: gate_dest_dir :default: {{ base_dir }}/openstack .. zuul:rolevar:: devstack_dir :default: {{ base_dir }}/openstack/devstack .. zuul:rolevar:: neutron_dir :default: {{ gate_dest_dir }}/neutron .. zuul:rolevar:: neutron_fwaas_dir :default: {{ gate_dest_dir }}/neutron-fwaas ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.341352 neutron-fwaas-20.0.0/roles/configure_functional_tests/defaults/0000775000175000017500000000000000000000000024765 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/roles/configure_functional_tests/defaults/main.yaml0000664000175000017500000000045100000000000026575 0ustar00zuulzuul00000000000000tests_venv: "{{ tox_envlist }}" project_name: "neutron" base_dir: "{{ ansible_user_dir }}/src/opendev.org" gate_dest_dir: "{{ base_dir }}/openstack" devstack_dir: "{{ base_dir }}/openstack/devstack" neutron_dir: "{{ gate_dest_dir }}/neutron" neutron_fwaas_dir: "{{ gate_dest_dir }}/neutron-fwaas" ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.341352 neutron-fwaas-20.0.0/roles/configure_functional_tests/tasks/0000775000175000017500000000000000000000000024303 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/roles/configure_functional_tests/tasks/main.yaml0000664000175000017500000000117600000000000026120 0ustar00zuulzuul00000000000000- shell: cmd: | set -e set -x GATE_STACK_USER={{ ansible_user }} IS_GATE=True BASE_DIR={{ base_dir }} GATE_DEST={{ gate_dest_dir }} PROJECT_NAME={{ project_name }} NEUTRON_PATH={{ neutron_dir }} NEUTRON_FWAAS_PATH={{ neutron_fwaas_dir }} DEVSTACK_PATH={{ devstack_dir }} VENV={{ tests_venv }} source $DEVSTACK_PATH/functions source $DEVSTACK_PATH/lib/neutron_plugins/ovs_source source $NEUTRON_FWAAS_PATH/tools/configure_for_fwaas_func_testing.sh configure_host_for_fwaas_func_testing executable: /bin/bash ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.341352 neutron-fwaas-20.0.0/setup.cfg0000664000175000017500000000607000000000000016231 0ustar00zuulzuul00000000000000[metadata] name = neutron-fwaas summary = OpenStack Networking FWaaS description_file = README.rst author = OpenStack author_email = openstack-discuss@lists.openstack.org home_page = https://docs.openstack.org/neutron-fwaas/latest/ python_requires = >=3.8 classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: POSIX :: Linux Programming Language :: Python Programming Language :: Python :: 3 Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 Programming Language :: Python :: 3 :: Only [files] packages = neutron_fwaas data_files = etc/neutron/rootwrap.d = etc/neutron/rootwrap.d/fwaas-privsep.filters [entry_points] firewall_drivers = iptables_v2 = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.iptables_fwaas_v2:IptablesFwaasDriver neutron.service_plugins = firewall_v2 = neutron_fwaas.services.firewall.fwaas_plugin_v2:FirewallPluginV2 neutron.db.alembic_migrations = neutron-fwaas = neutron_fwaas.db.migration:alembic_migrations oslo.config.opts = neutron.fwaas = neutron_fwaas.opts:list_opts firewall.agent = neutron_fwaas.opts:list_agent_opts oslo.policy.policies = neutron-fwaas = neutron_fwaas.policies:list_rules neutron.policies = neutron-fwaas = neutron_fwaas.policies:list_rules neutron.agent.l2.extensions = fwaas_v2 = neutron_fwaas.services.firewall.service_drivers.agents.l2.fwaas_v2:FWaaSV2AgentExtension neutron.agent.l2.firewall_drivers = noop = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.noop.noop_driver:NoopFirewallL2Driver ovs = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.l2.openvswitch_firewall.firewall:OVSFirewallDriver neutron.agent.l3.extensions = fwaas_v2 = neutron_fwaas.services.firewall.service_drivers.agents.l3reference.firewall_l3_agent_v2:L3WithFWaaS fwaas_v2_log = neutron_fwaas.services.logapi.agents.l3.fwg_log:FWaaSL3LoggingExtension neutron.agent.l3.firewall_drivers = conntrack = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.legacy_conntrack:ConntrackLegacy netlink_conntrack = neutron_fwaas.services.firewall.service_drivers.agents.drivers.linux.netlink_conntrack:ConntrackNetlink neutron.services.logapi.drivers = fwaas_v2_log = neutron_fwaas.services.logapi.agents.drivers.iptables.log:IptablesLoggingDriver console_scripts = neutron-fwaas-migrate-v1-to-v2 = neutron_fwaas.cmd.v1_to_v2_db_migration:main neutron.status.upgrade.checks = neutron_fwaas = neutron_fwaas.cmd.upgrade_checks.checks:Checks [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = neutron_fwaas/locale/neutron_fwaas.pot [compile_catalog] directory = neutron_fwaas/locale domain = neutron_fwaas [update_catalog] domain = neutron_fwaas output_dir = neutron_fwaas/locale input_file = neutron_fwaas/locale/neutron_fwaas.pot [egg_info] tag_build = tag_date = 0 ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/setup.py0000664000175000017500000000127200000000000016121 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=1712145087.0 neutron-fwaas-20.0.0/test-requirements.txt0000664000175000017500000000100500000000000020642 0ustar00zuulzuul00000000000000hacking>=6.1.0,<6.2.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0 python-subunit>=1.0.0 # Apache-2.0/BSD requests-mock>=1.2.0 # Apache-2.0 oslo.concurrency>=3.26.0 # Apache-2.0 stestr>=1.0.0 # Apache-2.0 testresources>=2.0.0 # Apache-2.0/BSD testtools>=2.2.0 # MIT testscenarios>=0.4 # Apache-2.0/BSD WebOb>=1.8.2 # MIT WebTest>=2.0.27 # MIT oslotest>=3.2.0 # Apache-2.0 pylint==2.17.4 # GPLv2 PyMySQL>=0.7.6 # MIT License psycopg2>=2.7.3 # LGPL/ZPL ddt>=1.0.1 # MIT doc8>=0.6.0 # Apache-2.0 Pygments>=2.2.0 # BSD ././@PaxHeader0000000000000000000000000000003300000000000011451 xustar000000000000000027 mtime=1712145114.341352 neutron-fwaas-20.0.0/tools/0000775000175000017500000000000000000000000015545 5ustar00zuulzuul00000000000000././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/tools/check_unit_test_structure.sh0000775000175000017500000000311500000000000023377 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_fwaas/tests/unit test_path=$neutron_path/$base_test_path test_files=$(find ${test_path} -iname 'test_*.py') ignore_regexes=( "^plugins.*$", "^misc.*$" ) 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_fwaas/$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=1712145087.0 neutron-fwaas-20.0.0/tools/clean.sh0000775000175000017500000000030400000000000017163 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=1712145087.0 neutron-fwaas-20.0.0/tools/configure_for_func_testing.sh0000775000175000017500000002141600000000000023507 0ustar00zuulzuul00000000000000#!/usr/bin/env bash # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 # Control variable used to determine whether to execute this script # directly or allow the gate_hook to import. IS_GATE=${IS_GATE:-False} USE_CONSTRAINT_ENV=${USE_CONSTRAINT_ENV:-True} if [[ "$IS_GATE" != "True" ]] && [[ "$#" -lt 1 ]]; then >&2 echo "Usage: $0 /path/to/devstack [-i] Configure a host to run Neutron's functional test suite. -i Install Neutron's package dependencies. By default, it is assumed that devstack has already been used to deploy neutron-fwaas to the target host and that package dependencies need not be installed. Warning: This script relies on devstack to perform extensive modification to the underlying host. It is recommended that it be invoked only on a throw-away VM." exit 1 fi # Skip the first argument OPTIND=2 while getopts ":i" opt; do case $opt in i) INSTALL_BASE_DEPENDENCIES=True ;; esac done # Default to environment variables to permit the gate_hook to override # when sourcing. VENV=${VENV:-dsvm-functional} DEVSTACK_PATH=${DEVSTACK_PATH:-$1} PROJECT_NAME=${PROJECT_NAME:-neutron-fwaas} REPO_BASE=${GATE_DEST:-$(cd $(dirname "$0")/../.. && pwd)} INSTALL_MYSQL_ONLY=${INSTALL_MYSQL_ONLY:-False} # The gate should automatically install dependencies. INSTALL_BASE_DEPENDENCIES=${INSTALL_BASE_DEPENDENCIES:-$IS_GATE} if [ ! -f "$DEVSTACK_PATH/stack.sh" ]; then >&2 echo "Unable to find devstack at '$DEVSTACK_PATH'. Please verify that the specified path points to a valid devstack repo." exit 1 fi set -x function _init { # Subsequently-called devstack functions depend on the following variables. HOST_IP=127.0.0.1 FILES=$DEVSTACK_PATH/files TOP_DIR=$DEVSTACK_PATH source $DEVSTACK_PATH/stackrc # Allow the gate to override values set by stackrc. DEST=${GATE_DEST:-$DEST} STACK_USER=${GATE_STACK_USER:-$STACK_USER} } function _install_base_deps { echo_summary "Installing base dependencies" INSTALL_TESTONLY_PACKAGES=True PACKAGES=$(get_packages general,neutron,q-agt,q-l3) # Do not install 'python-' prefixed packages other than # python-dev*. Neutron's functional testing relies on deployment # to a tox env so there is no point in installing python # dependencies system-wide. PACKAGES=$(echo $PACKAGES | perl -pe 's|python-(?!dev)[^ ]*||g') install_package $PACKAGES } function _install_rpc_backend { echo_summary "Installing rabbitmq" RABBIT_USERID=${RABBIT_USERID:-stackrabbit} RABBIT_HOST=${RABBIT_HOST:-$SERVICE_HOST} RABBIT_PASSWORD=${RABBIT_HOST:-secretrabbit} source $DEVSTACK_PATH/lib/rpc_backend enable_service rabbit install_rpc_backend restart_rpc_backend } # _install_databases [install_pg] function _install_databases { local install_pg=${1:-True} echo_summary "Installing databases" # Avoid attempting to configure the db if it appears to already # have run. The setup as currently defined is not idempotent. if mysql openstack_citest > /dev/null 2>&1 < /dev/null; then echo_summary "DB config appears to be complete, skipping." return 0 fi MYSQL_PASSWORD=${MYSQL_PASSWORD:-secretmysql} DATABASE_PASSWORD=${DATABASE_PASSWORD:-secretdatabase} source $DEVSTACK_PATH/lib/database enable_service mysql initialize_database_backends install_database configure_database_mysql if [[ "$install_pg" == "True" ]]; then enable_service postgresql initialize_database_backends install_database configure_database_postgresql fi # Set up the 'openstack_citest' user and database in each backend tmp_dir=$(mktemp -d) trap "rm -rf $tmp_dir" EXIT cat << EOF > $tmp_dir/mysql.sql CREATE DATABASE openstack_citest; CREATE USER 'openstack_citest'@'localhost' IDENTIFIED BY 'openstack_citest'; CREATE USER 'openstack_citest' IDENTIFIED BY 'openstack_citest'; GRANT ALL PRIVILEGES ON *.* TO 'openstack_citest'@'localhost'; GRANT ALL PRIVILEGES ON *.* TO 'openstack_citest'; FLUSH PRIVILEGES; EOF /usr/bin/mysql -u root < $tmp_dir/mysql.sql if [[ "$install_pg" == "True" ]]; then cat << EOF > $tmp_dir/postgresql.sql CREATE USER openstack_citest WITH CREATEDB LOGIN PASSWORD 'openstack_citest'; CREATE DATABASE openstack_citest WITH OWNER openstack_citest; EOF # User/group postgres needs to be given access to tmp_dir setfacl -m g:postgres:rwx $tmp_dir sudo -u postgres /usr/bin/psql --file=$tmp_dir/postgresql.sql fi } function _install_agent_deps { echo_summary "Installing agent dependencies" ENABLED_SERVICES=q-agt,q-dhcp,q-l3 install_neutron_agent_packages } # Set up the rootwrap sudoers for neutron to target the rootwrap # configuration deployed in the venv. function _install_rootwrap_sudoers { echo_summary "Installing rootwrap sudoers file" PROJECT_VENV=$REPO_BASE/$PROJECT_NAME/.tox/$VENV ROOTWRAP_SUDOER_CMD="$PROJECT_VENV/bin/neutron-rootwrap $PROJECT_VENV/etc/neutron/rootwrap.conf *" ROOTWRAP_DAEMON_SUDOER_CMD="$PROJECT_VENV/bin/neutron-rootwrap-daemon $PROJECT_VENV/etc/neutron/rootwrap.conf" TEMPFILE=$(mktemp) cat << EOF > $TEMPFILE # A bug in oslo.rootwrap [1] prevents commands executed with 'ip netns # exec' from being automatically qualified with a prefix from # rootwrap's configured exec_dirs. To work around this problem, add # the venv bin path to a user-specific secure_path. # # While it might seem preferable to set a command-specific # secure_path, this would only ensure the correct path for 'ip netns # exec' and the command targeted for execution in the namespace would # not inherit the path. # # 1: https://bugs.launchpad.net/oslo.rootwrap/+bug/1417331 # Defaults:$STACK_USER secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PROJECT_VENV/bin" $STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_SUDOER_CMD $STACK_USER ALL=(root) NOPASSWD: $ROOTWRAP_DAEMON_SUDOER_CMD EOF chmod 0440 $TEMPFILE sudo chown root:root $TEMPFILE # Name the functional testing rootwrap to ensure that it will be # loaded after the devstack rootwrap (50_stack_sh if present) so # that the functional testing secure_path (a superset of what # devstack expects) will not be overwritten. sudo mv $TEMPFILE /etc/sudoers.d/60-neutron-func-test-rootwrap } function _install_post_devstack { echo_summary "Performing post-devstack installation" _install_databases _install_rootwrap_sudoers if is_ubuntu; then install_package isc-dhcp-client install_package netcat-openbsd elif is_fedora; then install_package dhclient else exit_distro_not_supported "installing dhclient package" fi # Installing python-openvswitch from packages is a stop-gap while # python-openvswitch remains unavailable from pypi. This also # requires that sitepackages=True be set in tox.ini to allow the # venv to use the installed package. Once python-openvswitch # becomes available on pypi, this will no longer be required. # # NOTE: the package name 'python-openvswitch' is common across # supported distros. install_package python-openvswitch enable_kernel_bridge_firewall } function _configure_iptables_rules { # For linuxbridge agent fullstack tests we need to add special rules to # iptables for connection of agents to rabbitmq: CHAIN_NAME="openstack-INPUT" sudo iptables -n --list $CHAIN_NAME 1> /dev/null 2>&1 || CHAIN_NAME="INPUT" sudo iptables -I $CHAIN_NAME -s 240.0.0.0/8 -p tcp -m tcp -d 240.0.0.0/8 --dport 5672 -j ACCEPT } function configure_host_for_func_testing { echo_summary "Configuring host for functional testing" if [[ "$INSTALL_BASE_DEPENDENCIES" == "True" ]]; then # Installing of the following can be achieved via devstack by # installing neutron, so their installation is conditional to # minimize the work to do on a devstack-configured host. _install_base_deps _install_agent_deps _install_rpc_backend fi _install_post_devstack } _init if [[ "$IS_GATE" != "True" ]]; then if [[ "$INSTALL_MYSQL_ONLY" == "True" ]]; then _install_databases nopg else configure_host_for_func_testing fi fi if [[ "$VENV" =~ "dsvm-fullstack" ]]; then _configure_iptables_rules fi ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/tools/configure_for_fwaas_func_testing.sh0000775000175000017500000000152200000000000024664 0ustar00zuulzuul00000000000000set -e IS_GATE=${IS_GATE:-False} USE_CONSTRAINT_ENV=${USE_CONSTRAINT_ENV:-False} PROJECT_NAME=${PROJECT_NAME:-neutron-fwaas} REPO_BASE=${GATE_DEST:-$(cd $(dirname "$BASH_SOURCE")/../.. && pwd)} source $REPO_BASE/neutron/tools/configure_for_func_testing.sh NEUTRON_FWAAS_DIR=$REPO_BASE/neutron-fwaas source $NEUTRON_FWAAS_DIR/devstack/plugin.sh function _install_fw_package { echo_summary "Installing fw packs" if is_ubuntu; then install_package conntrack else # EPEL install_package conntrack-tools fi } function configure_host_for_fwaas_func_testing { echo_summary "Configuring for Fwaas functional testing" if [ "$IS_GATE" == "True" ]; then configure_host_for_func_testing fi _install_fw_package } if [ "$IS_GATE" != "True" ]; then configure_host_for_fwaas_func_testing fi ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/tools/deploy_rootwrap.sh0000775000175000017500000000420400000000000021335 0ustar00zuulzuul00000000000000#!/usr/bin/env bash # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # 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 -eu if [ "$#" -ne 3 ]; then >&2 echo "Usage: $0 /path/to/neutron_fwaas /path/to/target/etc /path/to/target/bin Deploy Neutron FWaaS's rootwrap configuration. Warning: Any existing rootwrap files at the specified etc path will be removed by this script. Optional: set OS_SUDO_TESTING=1 to deploy the filters required by Neutron's functional testing suite." exit 1 fi OS_SUDO_TESTING=${OS_SUDO_TESTING:-0} neutron_path=${OS_NEUTRON_PATH} fwaas_path=$1 target_etc_path=$2 target_bin_path=$3 src_conf_path=${neutron_path}/etc src_conf=${src_conf_path}/rootwrap.conf src_rootwrap_path=${src_conf_path}/neutron/rootwrap.d fwaas_src_conf_path=${fwaas_path}/etc fwaas_src_rootwrap_path=${fwaas_src_conf_path}/neutron/rootwrap.d dst_conf_path=${target_etc_path}/neutron dst_conf=${dst_conf_path}/rootwrap.conf dst_rootwrap_path=${dst_conf_path}/rootwrap.d if [[ -d "$dst_rootwrap_path" ]]; then rm -rf ${dst_rootwrap_path} fi mkdir -p -m 755 ${dst_rootwrap_path} cp -p ${src_rootwrap_path}/* ${fwaas_src_rootwrap_path}/* ${dst_rootwrap_path}/ cp -p ${src_conf} ${dst_conf} sed -i "s:^filters_path=.*$:filters_path=${dst_rootwrap_path}:" ${dst_conf} sed -i "s:^\(exec_dirs=.*\)$:\1,${target_bin_path}:" ${dst_conf} if [[ "$OS_SUDO_TESTING" = "1" ]]; then sed -i 's/use_syslog=False/use_syslog=True/g' ${dst_conf} sed -i 's/syslog_log_level=ERROR/syslog_log_level=DEBUG/g' ${dst_conf} cp -p ${neutron_path}/neutron/tests/contrib/testing.filters \ ${dst_rootwrap_path}/ cp -p ${fwaas_path}/neutron_fwaas/tests/contrib/functional-testing.filters \ ${dst_rootwrap_path}/ fi ././@PaxHeader0000000000000000000000000000002600000000000011453 xustar000000000000000022 mtime=1712145087.0 neutron-fwaas-20.0.0/tools/generate_config_file_samples.sh0000775000175000017500000000144000000000000023745 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=1712145087.0 neutron-fwaas-20.0.0/tox.ini0000664000175000017500000001477700000000000015740 0ustar00zuulzuul00000000000000[tox] envlist = py39,pep8,pylint minversion = 3.18.0 ignore_basepython_conflict = True [testenv] basepython = {env:TOX_PYTHON:python3} setenv = VIRTUAL_ENV={envdir} PYTHONWARNINGS=default::DeprecationWarning OS_LOG_CAPTURE={env:OS_LOG_CAPTURE:true} OS_STDOUT_CAPTURE={env:OS_STDOUT_CAPTURE:true} OS_STDERR_CAPTURE={env:OS_STDERR_CAPTURE:true} usedevelop = True deps = -c{env:UPPER_CONSTRAINTS_FILE:https://opendev.org/openstack/requirements/raw/branch/master/upper-constraints.txt} -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt allowlist_externals = bash find commands = find . -type f -name "*.py[c|o]" -delete find . -path "*/__pycache__*" -delete stestr run {posargs} [testenv:common] # Fake job to define environment variables shared between dsvm/non-dsvm jobs setenv = OS_TEST_TIMEOUT=180 commands = false [testenv:dsvm] # Fake job to define environment variables shared between dsvm jobs setenv = OS_SUDO_TESTING=1 OS_ROOTWRAP_CMD=sudo {envdir}/bin/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf OS_ROOTWRAP_DAEMON_CMD=sudo {envdir}/bin/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf OS_FAIL_ON_MISSING_DEPS=1 OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs} commands = false [testenv:functional] setenv = {[testenv]setenv} {[testenv:common]setenv} OS_TEST_PATH=./neutron_fwaas/tests/functional OS_LOG_PATH={env:OS_LOG_PATH:/opt/stack/logs} commands = stestr run {posargs} [testenv:dsvm-fullstack-gate] setenv = {[testenv]setenv} {[testenv:common]setenv} {[testenv:dsvm]setenv} OS_NEUTRON_PATH={env:OS_NEUTRON_PATH:/home/zuul/src/opendev.org/openstack/neutron} # workaround for DB teardown lock contention (bug/1541742) OS_TEST_TIMEOUT={env:OS_TEST_TIMEOUT:600} OS_TEST_PATH=./neutron_fwaas/tests/fullstack commands = {toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin stestr run --concurrency 4 {posargs} [testenv:api] sitepackages=True setenv = OS_TEST_PATH=./neutron_fwaas/tests/tempest_plugin/tests/api/ OS_TESTR_CONCURRENCY=1 TEMPEST_CONFIG_DIR={env:TEMPEST_CONFIG_DIR:/opt/stack/tempest/etc} commands = stestr run {posargs} [testenv:scenario] sitepackages=True setenv = OS_TEST_PATH=./neutron_fwaas/tests/tempest_plugin/tests/scenario/ OS_TESTR_CONCURRENCY=1 TEMPEST_CONFIG_DIR={env:TEMPEST_CONFIG_DIR:/opt/stack/tempest/etc} commands = stestr run {posargs} [testenv:dsvm-functional-gate] setenv = OS_TEST_PATH=./neutron_fwaas/tests/functional OS_SUDO_TESTING=1 OS_ROOTWRAP_CMD=sudo {envdir}/bin/neutron-rootwrap {envdir}/etc/neutron/rootwrap.conf OS_ROOTWRAP_DAEMON_CMD=sudo {envdir}/bin/neutron-rootwrap-daemon {envdir}/etc/neutron/rootwrap.conf OS_FAIL_ON_MISSING_DEPS=1 OS_NEUTRON_PATH={env:OS_NEUTRON_PATH:/home/zuul/src/opendev.org/openstack/neutron} allowlist_externals = bash cp sudo commands = {toxinidir}/tools/deploy_rootwrap.sh {toxinidir} {envdir}/etc {envdir}/bin stestr run {posargs} [testenv:releasenotes] deps = -r{toxinidir}/doc/requirements.txt commands = sphinx-build -a -E -W -d releasenotes/build/doctrees -b html releasenotes/source releasenotes/build/html [testenv:pep8] commands = flake8 doc8 {posargs} bash {toxinidir}/tools/check_unit_test_structure.sh neutron-db-manage --subproject neutron-fwaas --database-connection sqlite:// check_migration {[testenv:genconfig]commands} {[testenv:genpolicy]commands} allowlist_externals = bash [testenv:cover] setenv = VIRTUAL_ENV={envdir} LANGUAGE=en_US PYTHON=coverage run --source neutron_fwaas --omit='*tests*' --parallel-mode commands = coverage erase stestr run {posargs} coverage combine coverage report --skip-covered --omit='*test*' coverage html -d cover coverage xml -o cover/coverage.xml [testenv:venv] commands = {posargs} deps = -r{toxinidir}/doc/requirements.txt [testenv:docs] # Do not remove requirements.txt from deps list as without it # upper constraints will not be used for deps listed in requirements.txt # and may cause issues deps = -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} -r{toxinidir}/doc/requirements.txt -r{toxinidir}/requirements.txt allowlist_externals = rm commands = rm -rf doc/source/contributor/api sphinx-build -W -b html doc/source doc/build/html [testenv:pdf-docs] deps = {[testenv:docs]deps} allowlist_externals = rm make commands = rm -rf doc/source/contributor/api sphinx-build -W -b latex doc/source doc/build/pdf make -C doc/build/pdf [doc8] ignore = D000 ignore-path = .venv,.git,.tox,.tmp,*neutron_fwaas/locale*,*lib/python*,neutron_fwaas.egg*,doc/build,releasenotes/*,doc/source/contributor/api,requirements.txt,test-requirements.txt [flake8] # N530 direct neutron imports not allowed # W504 Line break occurred after a binary operator # E126 continuation line over-indented for hanging indent # E128 continuation line under-indented for visual indent # H405 multi line docstring summary not separated with an empty line # I202 Additional newline in a group of imports # E731 do not assign a lambda expression, use a def # W504 line break after binary operator ignore = E126,E128,E731,I202,H405,N530,W504 # H106: Don't put vim configuration in source files # H203: Use assertIs(Not)None to check for None # H204: Use assert(Not)Equal to check for equality # H205: Use assert(Greater|Less)(Equal) for comparison # H904: Delay string interpolations at logging calls enable-extensions=H106,H203,H204,H205,H904 show-source = true exclude = .venv,.git,.tox,dist,doc,*lib/python*,.tmp,*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_fwaas} [hacking] import_exceptions = neutron_fwaas._i18n local-check-factory = neutron_lib.hacking.checks.factory [testenv:genconfig] commands = bash {toxinidir}/tools/generate_config_file_samples.sh [testenv:genpolicy] commands = oslopolicy-sample-generator --config-file=etc/oslo-policy-generator/policy.conf [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://git.openstack.org/openstack/neutron#egg=neutron" [testenv:py3-dev] commands = {[testenv:dev]commands} {[testenv]commands} [testenv:pep8-dev] deps = {[testenv]deps} commands = {[testenv:dev]commands} {[testenv:pep8]commands}