networking-hyperv-2.0.0/0000775000567000056710000000000012677524665016400 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/.coveragerc0000664000567000056710000000015412677524354020514 0ustar jenkinsjenkins00000000000000[run] branch = True source = hyperv omit = hyperv/tests/*,hyperv/openstack/* [report] ignore_errors = True networking-hyperv-2.0.0/doc/0000775000567000056710000000000012677524665017145 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/doc/specs/0000775000567000056710000000000012677524665020262 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/doc/specs/scale-hyperv-neutron-agent.rst0000664000567000056710000002037612677524354026205 0ustar jenkinsjenkins00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode =========================== Scale Hyper-V Neutron Agent =========================== https://blueprints.launchpad.net/networking-hyperv/+spec/scale-hyperv-neutron-agent A typical medium-sized hybrid cloud deployment consists of more than 50 Hyper-V compute nodes along with computes like KVM or ESX. The rate at which VMs are spawned/updated under such a deployment is around 25 operations/minute. And these operations consists of spawning, updating and deleting of the VMs and their properties (like security group rules). At this rate the possibility of concurrent spawn or update operations on a given compute is High. What is typically observed is a spawn rate of ~2 VM(s)/minute. Since WMI is not that performant, a VM port binding in Hyper-V neutron agent takes 10x amount of time when compared to KVM IPtables. The situation worsens when the number of SG Rules to apply increases for a given port (with the number of SG members), and there are many ports in queue to treat. Under such a scenario neutron agent running on Hyper-v compute fails to complete binding security rules to the VM port in given time, and VM remains inaccessible on the allocated IP address. This blueprint addresses the Neutron Hyper-V Agent's port binding rate by introducing port binding concurrency. Problem Description =================== Under enterprise class cloud environment the possibility of single compute receiving more than one VM spawn request grows. It is the nova scheduler that chooses the compute node on which the VM will be spawned. The neutron part on compute node runs as an independent task which does the port related configuration for the spawned VM. Today, neutron agent runs in a single threaded environment, the main thread is responsible for doing the port binding (i.e. vlan configuration and applying port rules) for the spawned VM and sending agent keep alive message to controller, while green threads are responsible for processing the port updates (i.e. updating port acls/rules). The threading mechanism is implemented using python's green thread library, the green thread by nature operated in run until completion or preemption mode, which means that a green thread will not yield the CPU until it completes its job or it is preempted explicitly. The above mentioned nature of green thread impacts the Hyper-V scale. The problem starts when a compute already has around 15 VMs hosted and security group update is in process, at the same time neutron agent's deamon loop wakes up and finds that there were ports added for which binding is pending. Because the update thread is holding the CPU, the port binding main thread will not get turn to execute resulting in delayed port binding. Since the nova-compute service runs in isolation independent of neutron, it will not wait for neutron to complete port binding and will power on the VM. The booted VM will start sending the DHCP discovery which ultimately gets dropped resulting in VM not getting DHCP IP. The problem becomes worse with growing number of VMs because more VMs in network mean more time to complete port update, and the list of added ports pending for port binding also grows due to arrival of new VMs. Proposed Change =============== This blueprint proposes solution to the above discussed problem in two parts. **Part 1.** The Hyper-V Neutron Agent and the nova-compute service can be syncronized, which can help solve the first part of the problem: the VMs are currently starting before the neutron ports are properly bound. By waiting for the ports to be processed, the VMs will be able to properly aquire the DHCP replies. Currently, Neutron generates a `vif plugged` notification when a port has been reported as `up` (``update_device_up``), which is already done by the Neutron Hyper-V Agent when it finished processing a port. The implementation for waiting for the mentioned notification event in the nova Hyper-V Driver will be addressed by the blueprint [1]. **Part 2.** The second part of the proposal is to improve the logic behind the Neutron Hyper-V Agent's port processing. In this regard, there are a couple of things that can be done. **a.** Replace WMI. Performance-wise, WMI is notoriously bad. In order to address this, the PyMI module has been created and it will be used instead [2]. PyMI is a drop-in replacement of WMI, as it maintains the same interface, via its WMI wrapper, meaning that PyMI can be used on any previous, current and future branches of networking-hyperv. It has been been observed that PyMI reduces the execution time by roughly 2.0-2.2X, compared to the old WMI. **b.** Implement vNIC creation / deletion event listeners. Currently, the agent periodically polls for all the present vNICs on the host (which can be an expensive operation when there are hundreds of vNICs) and then query the Neutron server for port details for all of them. This is repeated if the port binding failed even for one of them. By implementing the vNIC creation / deletion event listeners, querying all the vNICs is no longer necessary. Furthermore, the Neutron server will not have to be queried for all of the vNICs when a single one of them failed to be bound, reducing the load on the Neutron server. **c.** Parallel port binding. Currently, the ports are being processed sequencially. Processing them in parallel can lead to a performance boost. Plus, PyMI was built while having parallelism in mind, as oposed to the old WMI, meaning that the performance gain by using both PyMI and parallel port binding will be even greater. We will be using Native Threads for the purpose of port binding, as they can span multiple processors (green threads do not). On a host with 32 cores, using 10 Native Threads as workers + PyMI has a ~6X better performance than the previous, single-threaded processing using PyMI, leading to a total ~12X improvement over the single-threaded processing using WMI. It is notable to mention that there a very small performance gain between 10 Native Thread workers and 20 (~5%). As a recommendation the best experience, the number of workers should be set between 10 and 15, or the number of cores on the host, whichever is lowest. Data Model Impact ----------------- None REST API Impact --------------- None Security Impact --------------- None Notifications Impact -------------------- None Other End User Impact --------------------- None Performance Impact ------------------ This blueprint will improve the Hyper-V neutron agent performance. IPv6 Impact ----------- None Other Deployer Impact --------------------- The number of Native Thread workers can be set in the ``worker_count`` configuration option in ``neutron-hyperv-agent.conf``. As default, it is set to 10. Developer Impact ---------------- None Community Impact ---------------- Scaling Openstack neutron is always a challenge and this change will allow Hyper-V neutron to scale around 1000 VM with 10 tenants. Alternatives ------------ None Implementation ============== Assignee(s) ----------- Primary assignee: Other contributors: Work Items ---------- * Implementing vNIC creation / deletion event listeners. * Implementing Native Thread workers. * Writing unit test. * Functionality testing. * Scale testing. Dependencies ============ * Nova to process neutron vif notification. Testing ======= The changes will be tested by deploying cloud with around 20 computes nodes and spawning 1000 VMs at concurrency of 6 VMs per minute for overall cloud with 10 tenants each having their own network. Tempest Tests ------------- TBD Functional Tests ---------------- TBD API Tests --------- None Documentation Impact ==================== None User Documentation ------------------ Nova boot time may increase due to Neutron to Nova notification, the delay could be seen when there are large number of security groups rules associated with a port. Developer Documentation ----------------------- None References ========== [1] Hyper-V Spawn on Neutron Event nova blueprint: https://blueprints.launchpad.net/nova/+spec/hyper-v-spawn-on-neutron-event [2] PyMI github repository: https://github.com/cloudbase/PyMI/ networking-hyperv-2.0.0/doc/specs/hyper-v-nvgre.rst0000664000567000056710000001021412677524354023516 0ustar jenkinsjenkins00000000000000.. This work is licensed under a Creative Commons Attribution 3.0 Unported License. http://creativecommons.org/licenses/by/3.0/legalcode ======================================== Hyper-V Neutron Agent NVGRE network type ======================================== https://blueprints.launchpad.net/networking-hyperv/+spec/hyper-v-nvgre Hyper-V Network Virtualization (HNV) was first introduced in Windows Hyper-V / Server 2012 and has the purpose of enabling the virtualization of Layer 2 and Layer 3 networking models. One of the HNV configuration approches is called NVGRE (Network Virtualization through GRE). [1] Problem Description =================== NVGRE can be used between Windows Hyper-V / Server 2012 and Windows Hyper-V / Server 2012 R2 VMs, but the usage can be extended to other hypervisors which support GRE by using OpenVSwitch. Proposed Change =============== In order to implement this feature, there are a few considerations things that need to be kept in mind: * NVGRE does not exist prior to Windows / Hyper-V Server 2012. The implementation will have to make sure it won't break the Hyper-V Neutron Agent on a Windows / Hyper-V 2008 R2 compute node. * HNV is not enabled by default in Windows / Hyper-V Server 2012. * The vSwitch used for the NVGRE tunneling must have the Alow Management OS flag turned off. * Additional information is needed from Neutron in order to for the feature to behave as expected. In order to retrieve the information, Neutron credentials are necessary. * The network's segmentation_id, or the NVGRE's equivalent, VirtualSubnetId has to be higher than 4095. Hyper-V cannot create Customer Routes or Lookup Records if the SegmentationId is lower or equal to 4095. * The NVGRE network cannot have a gateway ending in '.1', as Hyper-V does not allow it. Any other gateway (including networks without a gateway) is acceptable. * Only one subnet per network. The reason is that it cannot be created more Customer Routes for the same VirtualSubnetID. Adding new routes for the same VirtualSubnetId will cause exceptions. * Lookup Records should be added for the metadata address (default is 169.254.169.254) in order for instances to properly fetch their metadata. * Lookup Records should be added for 0.0.0.0. One reason why they're necessary is that they are required in order to receive DHCP offers. * ProviderAddress, ProviderRoute, CustomerRoute and LookupRecord WMI objects are not persistent. Which means they will not exist after the host restarts. Configuration ------------- A few configuration options can be set in order for the feature to function properly. These configuration options are to be set in the [NVGRE] section of the .conf file: * enable_support (default=False). Enables Hyper-V NVGRE as a network type for the agent. * provider_vlan_id (default=0). The VLAN ID set to the physical network. * provider_tunnel_ip. Specifies the local IP which will be used for NVGRE tunneling. Work Items ---------- * NVGRE Utils classes, which uses the ``//./root/StandardCimv2`` WMI namespace. It will be responsible with creating the WMI objects required for the feature to function properly: ProviderAddress, ProviderRoute, CustomerRoute, Lookup Record objects; while considering the limitations described above. * Create local database in order to persist the above objects and load them when the agent starts. The database should be kept clean. * Create method to synchronize LookupRecords with other Hyper-V Neutron Agents that have NVGRE enabled, as they must exist on both ends of the NVGRE tunnel. * Class that retrieves necessary information from Neutron in order to correctly create the mentioned WMI objects. * The Hyper-V Neutron Agent should report the following agent configuration, if NVGRE is supported and enabled: - ``tunneling_ip``: the host's IP which is used as a ProviderAddress. - ``tunnel_types``: NVGRE * HypervMechanismDriver.get_allowed_network_types method should check the agent's reported ``tunnel_types`` and include it in the return value. * Implement NVGRE network type in Neutron. References ========== [1] https://technet.microsoft.com/en-us/library/JJ134174.aspx networking-hyperv-2.0.0/doc/source/0000775000567000056710000000000012677524665020445 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/doc/source/usage.rst0000664000567000056710000000013412677524354022274 0ustar jenkinsjenkins00000000000000======== Usage ======== To use networking-hyperv in a project:: import hyperv.neutron networking-hyperv-2.0.0/doc/source/readme.rst0000664000567000056710000000003612677524354022426 0ustar jenkinsjenkins00000000000000.. include:: ../../README.rst networking-hyperv-2.0.0/doc/source/conf.py0000664000567000056710000000463512677524354021747 0ustar jenkinsjenkins00000000000000# -*- 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. import os import sys sys.path.insert(0, os.path.abspath('../..')) # -- General configuration ---------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', #'sphinx.ext.intersphinx', 'oslosphinx' ] # autodoc generation is a bit aggressive and a nuisance when doing heavy # text edit cycles. # execute "export SPHINX_DEBUG=1" in your terminal to disable # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'networking-hyperv' copyright = '2013, OpenStack Foundation' # If true, '()' will be appended to :func: etc. cross-reference text. add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). add_module_names = True # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. # html_theme_path = ["."] # html_theme = '_theme' # html_static_path = ['static'] # Output file base name for HTML help builder. htmlhelp_basename = '%sdoc' % project # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass # [howto/manual]). latex_documents = [ ('index', '%s.tex' % project, '%s Documentation' % project, 'OpenStack Foundation', 'manual'), ] # Example configuration for intersphinx: refer to the Python standard library. #intersphinx_mapping = {'http://docs.python.org/': None} networking-hyperv-2.0.0/doc/source/index.rst0000664000567000056710000000100512677524354022275 0ustar jenkinsjenkins00000000000000.. networking-hyperv documentation master file, created by sphinx-quickstart on Tue Jul 9 22:26:36 2013. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to networking-hyperv's documentation! ======================================================== Contents: .. toctree:: :maxdepth: 2 readme installation usage contributing Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` networking-hyperv-2.0.0/doc/source/contributing.rst0000664000567000056710000000011312677524354023674 0ustar jenkinsjenkins00000000000000============ Contributing ============ .. include:: ../../CONTRIBUTING.rst networking-hyperv-2.0.0/doc/source/installation.rst0000664000567000056710000000033412677524354023673 0ustar jenkinsjenkins00000000000000============ Installation ============ At the command line:: $ pip install networking-hyperv Or, if you have virtualenvwrapper installed:: $ mkvirtualenv networking-hyperv $ pip install networking-hyperv networking-hyperv-2.0.0/requirements.txt0000664000567000056710000000101312677524354021652 0ustar jenkinsjenkins00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. pbr>=1.6 # Apache-2.0 Babel>=1.3 # BSD eventlet!=0.18.3,>=0.18.2 # MIT os-win>=0.2.3 # Apache-2.0 oslo.config>=3.7.0 # Apache-2.0 oslo.i18n>=2.1.0 # Apache-2.0 oslo.log>=1.14.0 # Apache-2.0 oslo.serialization>=1.10.0 # Apache-2.0 oslo.utils>=3.5.0 # Apache-2.0 python-neutronclient!=4.1.0,>=2.6.0 # Apache-2.0 networking-hyperv-2.0.0/networking_hyperv.egg-info/0000775000567000056710000000000012677524665023656 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/networking_hyperv.egg-info/entry_points.txt0000664000567000056710000000024612677524664027155 0ustar jenkinsjenkins00000000000000[console_scripts] neutron-hyperv-agent = hyperv.neutron.l2_agent:main [neutron.ml2.mechanism_drivers] hyperv = hyperv.neutron.ml2.mech_hyperv:HypervMechanismDriver networking-hyperv-2.0.0/networking_hyperv.egg-info/PKG-INFO0000664000567000056710000000677012677524664024764 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: networking-hyperv Version: 2.0.0 Summary: This project tracks the work to integrate the Hyper-V networking with Neutron. This project contains the Hyper-V Neutron Agent Mixin, Security Groups Driver, ML2 Mechanism Driver and the utils modules they use in order to properly bind neutron ports on a Hyper-V host. This project resulted from the neutron core vendor decomposition. Home-page: http://www.cloudbase.it/ Author: Cloudbase Solutions Srl Author-email: info@cloudbasesolutions.com License: Apache License, Version 2.0 Description: ================= networking-hyperv ================= This project tracks the work to integrate the Hyper-V networking with Neutron. This project contains the Hyper-V Neutron Agent Mixin, Security Groups Driver, ML2 Mechanism Driver and the utils modules they use in order to properly bind neutron ports on a Hyper-V host. This project resulted from the neutron core vendor decomposition. Supports Python 2.7 and Python 3.3. * Free software: Apache license * Documentation: http://docs.openstack.org/developer/networking-hyperv * Source: http://git.openstack.org/cgit/openstack/networking-hyperv * Bugs: http://bugs.launchpad.net/networking-hyperv How to Install -------------- Run the following command to install the agent in the system: :: C:\networking-hyperv> python setup.py install To properly use the agent, you will have to set the core_plugin in ``neutron.conf`` to: :: core_plugin = neutron.plugins.ml2.plugin.Ml2Plugin Additionally, you will have to add Hyper-V as a mechanism in ``ml2_conf.ini``: :: mechanism_drivers = openvswitch,hyperv Finally, make sure the tenant_network_types field contains network types supported by Hyper-V: local, flat, vlan. Tests ----- You will have to install the test dependencies first to be able to run the tests. :: C:\networking-hyperv> pip install -r test-requirements.txt You can run the unit tests with the following command. :: C:\networking-hyperv> nosetests hyperv\tests HACKING ------- To contribute to this repo, please go through the following steps. 1. Keep your working tree updated 2. Make modifications on your working tree 3. Run tests 4. If the tests pass, create a pull request on our github repo. 5. Wait for the pull request to be reviewed. Features -------- * TODO Keywords: openstack neutron hyper-v networking 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 :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 networking-hyperv-2.0.0/networking_hyperv.egg-info/top_level.txt0000664000567000056710000000000712677524664026404 0ustar jenkinsjenkins00000000000000hyperv networking-hyperv-2.0.0/networking_hyperv.egg-info/not-zip-safe0000664000567000056710000000000112677524657026105 0ustar jenkinsjenkins00000000000000 networking-hyperv-2.0.0/networking_hyperv.egg-info/requires.txt0000664000567000056710000000030212677524664026250 0ustar jenkinsjenkins00000000000000pbr>=1.6 Babel>=1.3 eventlet!=0.18.3,>=0.18.2 os-win>=0.2.3 oslo.config>=3.7.0 oslo.i18n>=2.1.0 oslo.log>=1.14.0 oslo.serialization>=1.10.0 oslo.utils>=3.5.0 python-neutronclient!=4.1.0,>=2.6.0 networking-hyperv-2.0.0/networking_hyperv.egg-info/dependency_links.txt0000664000567000056710000000000112677524664027723 0ustar jenkinsjenkins00000000000000 networking-hyperv-2.0.0/networking_hyperv.egg-info/SOURCES.txt0000664000567000056710000000324312677524665025544 0ustar jenkinsjenkins00000000000000.coveragerc .mailmap .testr.conf AUTHORS CONTRIBUTING.rst ChangeLog HACKING.rst LICENSE MANIFEST.in README.rst babel.cfg openstack-common.conf requirements.txt setup.cfg setup.py test-requirements.txt tox.ini doc/source/conf.py doc/source/contributing.rst doc/source/index.rst doc/source/installation.rst doc/source/readme.rst doc/source/usage.rst doc/specs/hyper-v-nvgre.rst doc/specs/scale-hyperv-neutron-agent.rst hyperv/__init__.py hyperv/common/__init__.py hyperv/common/i18n.py hyperv/neutron/__init__.py hyperv/neutron/config.py hyperv/neutron/constants.py hyperv/neutron/exception.py hyperv/neutron/hyperv_agent_notifier.py hyperv/neutron/hyperv_neutron_agent.py hyperv/neutron/l2_agent.py hyperv/neutron/neutron_client.py hyperv/neutron/nvgre_ops.py hyperv/neutron/security_groups_driver.py hyperv/neutron/ml2/README hyperv/neutron/ml2/__init__.py hyperv/neutron/ml2/mech_hyperv.py hyperv/tests/__init__.py hyperv/tests/base.py hyperv/tests/unit/__init__.py hyperv/tests/unit/neutron/__init__.py hyperv/tests/unit/neutron/test_hyperv_agent_notifier.py hyperv/tests/unit/neutron/test_hyperv_neutron_agent.py hyperv/tests/unit/neutron/test_l2_agent.py hyperv/tests/unit/neutron/test_mech_hyperv.py hyperv/tests/unit/neutron/test_neutron_client.py hyperv/tests/unit/neutron/test_nvgre_ops.py hyperv/tests/unit/neutron/test_security_groups_driver.py networking_hyperv.egg-info/PKG-INFO networking_hyperv.egg-info/SOURCES.txt networking_hyperv.egg-info/dependency_links.txt networking_hyperv.egg-info/entry_points.txt networking_hyperv.egg-info/not-zip-safe networking_hyperv.egg-info/pbr.json networking_hyperv.egg-info/requires.txt networking_hyperv.egg-info/top_level.txt tools/tox_install.shnetworking-hyperv-2.0.0/networking_hyperv.egg-info/pbr.json0000664000567000056710000000005612677524664025334 0ustar jenkinsjenkins00000000000000{"is_release": true, "git_version": "f0f7c18"}networking-hyperv-2.0.0/openstack-common.conf0000664000567000056710000000020512677524354022514 0ustar jenkinsjenkins00000000000000[DEFAULT] # The list of modules to copy from oslo-incubator.git # The base module to hold the copy of openstack.common base=hyperv networking-hyperv-2.0.0/LICENSE0000664000567000056710000002363712677524354017413 0ustar jenkinsjenkins00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. networking-hyperv-2.0.0/hyperv/0000775000567000056710000000000012677524665017715 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/neutron/0000775000567000056710000000000012677524665021407 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/neutron/exception.py0000664000567000056710000000126712677524354023760 0ustar jenkinsjenkins00000000000000# Copyright 2016 Cloudbase Solutions Srl # # 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. class NetworkingHyperVException(Exception): pass networking-hyperv-2.0.0/hyperv/neutron/__init__.py0000664000567000056710000000167212677524354023521 0ustar jenkinsjenkins00000000000000# Copyright (c) 2015 Cloudbase Solutions Srl # # 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 eventlet # eventlet monkey patching the os and thread modules causes subprocess.Popen # to fail on Windows when using pipes due to missing non-blocking IO support. # # bug report on eventlet: # https://bitbucket.org/eventlet/eventlet/issue/132/ # eventletmonkey_patch-breaks eventlet.monkey_patch(os=False, thread=False) networking-hyperv-2.0.0/hyperv/neutron/hyperv_neutron_agent.py0000664000567000056710000004613712677524354026234 0ustar jenkinsjenkins00000000000000# Copyright 2013 Cloudbase Solutions SRL # Copyright 2013 Pedro Navarro Perez # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import collections from concurrent import futures import inspect import re import threading import time from os_win import exceptions from os_win import utilsfactory from oslo_concurrency import lockutils from oslo_config import cfg from oslo_log import log as logging import six from hyperv.common.i18n import _, _LE, _LW, _LI # noqa from hyperv.neutron import constants from hyperv.neutron import exception from hyperv.neutron import nvgre_ops CONF = cfg.CONF CONF.import_group('NVGRE', 'hyperv.neutron.config') LOG = logging.getLogger(__name__) synchronized = lockutils.synchronized_with_prefix('n-hv-agent-') def _port_synchronized(f): # This decorator synchronizes operations targeting the same port. # The decorated method is expected to accept the port_id argument. def wrapper(*args, **kwargs): call_args = inspect.getcallargs(f, *args, **kwargs) port_id = call_args['port_id'] lock_name = 'port-lock-%s' % port_id @synchronized(lock_name) def inner(): return f(*args, **kwargs) return inner() return wrapper class HyperVNeutronAgentMixin(object): def __init__(self, conf=None): """Initializes local configuration of the Hyper-V Neutron Agent. :param conf: dict or dict-like object containing the configuration details used by this Agent. If None is specified, default values are used instead. conf format is as follows: { 'host': string, 'AGENT': {'polling_interval': int, 'local_network_vswitch': string, 'physical_network_vswitch_mappings': array, 'enable_metrics_collection': boolean, 'metrics_max_retries': int}, 'SECURITYGROUP': {'enable_security_group': boolean} } For more information on the arguments, their meaning and their default values, visit: http://docs.openstack.org/juno/config-reference/content/ networking-plugin-hyperv_agent.html """ super(HyperVNeutronAgentMixin, self).__init__() self._metricsutils = utilsfactory.get_metricsutils() self._utils = utilsfactory.get_networkutils() self._utils.init_caches() self._network_vswitch_map = {} self._port_metric_retries = {} self._nvgre_enabled = False conf = conf or {} agent_conf = conf.get('AGENT', {}) security_conf = conf.get('SECURITYGROUP', {}) self._host = conf.get('host', None) self._polling_interval = agent_conf.get('polling_interval', 2) self._local_network_vswitch = agent_conf.get('local_network_vswitch', 'private') self._worker_count = agent_conf.get('worker_count') self._phys_net_map = agent_conf.get( 'physical_network_vswitch_mappings', []) self.enable_metrics_collection = agent_conf.get( 'enable_metrics_collection', False) self._metrics_max_retries = agent_conf.get('metrics_max_retries', 100) self.enable_security_groups = security_conf.get( 'enable_security_group', False) self._load_physical_network_mappings(self._phys_net_map) self._init_nvgre() self._workers = futures.ThreadPoolExecutor(self._worker_count) def _load_physical_network_mappings(self, phys_net_vswitch_mappings): self._physical_network_mappings = collections.OrderedDict() for mapping in phys_net_vswitch_mappings: parts = mapping.split(':') if len(parts) != 2: LOG.debug('Invalid physical network mapping: %s', mapping) else: pattern = re.escape(parts[0].strip()).replace('\\*', '.*') vswitch = parts[1].strip() self._physical_network_mappings[pattern] = vswitch def _init_nvgre(self): # if NVGRE is enabled, self._nvgre_ops is required in order to properly # set the agent state (see get_agent_configrations method). if not CONF.NVGRE.enable_support: return if not CONF.NVGRE.provider_tunnel_ip: err_msg = _('enable_nvgre_support is set to True, but provider ' 'tunnel IP is not configured. Check neutron.conf ' 'config file.') LOG.error(err_msg) raise exception.NetworkingHyperVException(err_msg) self._nvgre_enabled = True self._nvgre_ops = nvgre_ops.HyperVNvgreOps( list(self._physical_network_mappings.values())) self._nvgre_ops.init_notifier(self.context, self.client) self._nvgre_ops.tunnel_update(self.context, CONF.NVGRE.provider_tunnel_ip, constants.TYPE_NVGRE) def _get_vswitch_for_physical_network(self, phys_network_name): for pattern in self._physical_network_mappings: if phys_network_name is None: phys_network_name = '' if re.match(pattern, phys_network_name): return self._physical_network_mappings[pattern] # Not found in the mappings, the vswitch has the same name return phys_network_name def _get_network_vswitch_map_by_port_id(self, port_id): for network_id, map in six.iteritems(self._network_vswitch_map): if port_id in map['ports']: return (network_id, map) # if the port was not found, just return (None, None) return (None, None) def network_delete(self, context, network_id=None): LOG.debug("network_delete received. " "Deleting network %s", network_id) # The network may not be defined on this agent if network_id in self._network_vswitch_map: self._reclaim_local_network(network_id) else: LOG.debug("Network %s not defined on agent.", network_id) def port_delete(self, context, port_id=None): LOG.debug("port_delete received") self._port_unbound(port_id) def port_update(self, context, port=None, network_type=None, segmentation_id=None, physical_network=None): LOG.debug("port_update received: %s", port['id']) if self._utils.vnic_port_exists(port['id']): self._treat_vif_port( port['id'], port['network_id'], network_type, physical_network, segmentation_id, port['admin_state_up']) else: LOG.debug("No port %s defined on agent.", port['id']) def tunnel_update(self, context, **kwargs): LOG.info(_LI('tunnel_update received: kwargs: %s'), kwargs) tunnel_ip = kwargs.get('tunnel_ip') if tunnel_ip == CONF.NVGRE.provider_tunnel_ip: # the notification should be ignored if it originates from this # node. return tunnel_type = kwargs.get('tunnel_type') self._nvgre_ops.tunnel_update(context, tunnel_ip, tunnel_type) def lookup_update(self, context, **kwargs): self._nvgre_ops.lookup_update(kwargs) def _get_vswitch_name(self, network_type, physical_network): if network_type != constants.TYPE_LOCAL: vswitch_name = self._get_vswitch_for_physical_network( physical_network) else: vswitch_name = self._local_network_vswitch return vswitch_name def _provision_network(self, port_id, net_uuid, network_type, physical_network, segmentation_id): LOG.info(_LI("Provisioning network %s"), net_uuid) vswitch_name = self._get_vswitch_name(network_type, physical_network) if network_type == constants.TYPE_VLAN: # Nothing to do pass elif network_type == constants.TYPE_NVGRE and self._nvgre_enabled: self._nvgre_ops.bind_nvgre_network( segmentation_id, net_uuid, vswitch_name) elif network_type == constants.TYPE_FLAT: # Nothing to do pass elif network_type == constants.TYPE_LOCAL: # TODO(alexpilotti): Check that the switch type is private # or create it if not existing pass else: raise exception.NetworkingHyperVException( (_("Cannot provision unknown network type %(network_type)s" " for network %(net_uuid)s") % dict(network_type=network_type, net_uuid=net_uuid))) map = { 'network_type': network_type, 'vswitch_name': vswitch_name, 'ports': [], 'vlan_id': segmentation_id} self._network_vswitch_map[net_uuid] = map def _reclaim_local_network(self, net_uuid): LOG.info(_LI("Reclaiming local network %s"), net_uuid) del self._network_vswitch_map[net_uuid] def _port_bound(self, port_id, net_uuid, network_type, physical_network, segmentation_id): LOG.debug("Binding port %s", port_id) if net_uuid not in self._network_vswitch_map: self._provision_network( port_id, net_uuid, network_type, physical_network, segmentation_id) map = self._network_vswitch_map[net_uuid] map['ports'].append(port_id) self._utils.connect_vnic_to_vswitch(map['vswitch_name'], port_id) if network_type == constants.TYPE_VLAN: LOG.info(_LI('Binding VLAN ID %(segmentation_id)s ' 'to switch port %(port_id)s'), dict(segmentation_id=segmentation_id, port_id=port_id)) self._utils.set_vswitch_port_vlan_id( segmentation_id, port_id) elif network_type == constants.TYPE_NVGRE and self._nvgre_enabled: self._nvgre_ops.bind_nvgre_port( segmentation_id, map['vswitch_name'], port_id) elif network_type == constants.TYPE_FLAT: # Nothing to do pass elif network_type == constants.TYPE_LOCAL: # Nothing to do pass else: LOG.error(_LE('Unsupported network type %s'), network_type) if self.enable_metrics_collection: self._utils.add_metrics_collection_acls(port_id) self._port_metric_retries[port_id] = self._metrics_max_retries def _port_unbound(self, port_id, vnic_deleted=False): (net_uuid, map) = self._get_network_vswitch_map_by_port_id(port_id) if not net_uuid: LOG.debug('Port %s was not found on this agent.', port_id) return LOG.debug("Unbinding port %s", port_id) self._utils.remove_switch_port(port_id, vnic_deleted) map['ports'].remove(port_id) if not map['ports']: self._reclaim_local_network(net_uuid) def _port_enable_control_metrics(self): if not self.enable_metrics_collection: return for port_id in list(self._port_metric_retries.keys()): try: if self._utils.is_metrics_collection_allowed(port_id): self._metricsutils.enable_port_metrics_collection(port_id) LOG.info(_LI('Port metrics enabled for port: %s'), port_id) del self._port_metric_retries[port_id] elif self._port_metric_retries[port_id] < 1: self._metricsutils.enable_port_metrics_collection(port_id) LOG.error(_LE('Port metrics raw enabling for port: %s'), port_id) del self._port_metric_retries[port_id] else: self._port_metric_retries[port_id] -= 1 except exceptions.NotFound: # the vNIC no longer exists. it might have been removed or # the VM it was attached to was destroyed. LOG.warning(_LW("Port %s no longer exists. Cannot enable " "metrics."), port_id) del self._port_metric_retries[port_id] def _update_ports(self, registered_ports): ports = self._utils.get_vnic_ids() if ports == registered_ports: return added = ports - registered_ports removed = registered_ports - ports return {'current': ports, 'added': added, 'removed': removed} @_port_synchronized def _treat_vif_port(self, port_id, network_id, network_type, physical_network, segmentation_id, admin_state_up): if admin_state_up: self._port_bound(port_id, network_id, network_type, physical_network, segmentation_id) # check if security groups is enabled. # if not, teardown the security group rules if self.enable_security_groups: self.sec_groups_agent.prepare_devices_filter([port_id]) else: self._utils.remove_all_security_rules(port_id) else: self._port_unbound(port_id) self.sec_groups_agent.remove_devices_filter([port_id]) def _process_added_port(self, device_details): device = device_details['device'] port_id = device_details['port_id'] try: self._treat_vif_port(port_id, device_details['network_id'], device_details['network_type'], device_details['physical_network'], device_details['segmentation_id'], device_details['admin_state_up']) LOG.debug("Updating port %s status as UP.", port_id) self.plugin_rpc.update_device_up(self.context, device, self.agent_id, self._host) LOG.info("Port %s processed.", port_id) except Exception: LOG.exception(_LE("Exception encountered while processing port " "%s."), port_id) # readd the port as "added", so it can be reprocessed. self._added_ports.add(device) def _treat_devices_added(self): try: devices_details_list = self.plugin_rpc.get_devices_details_list( self.context, self._added_ports, self.agent_id) except Exception as e: LOG.debug("Unable to get ports details for " "devices %(devices)s: %(e)s", {'devices': self._added_ports, 'e': e}) return for device_details in devices_details_list: device = device_details['device'] LOG.info(_LI("Adding port %s"), device) if 'port_id' in device_details: LOG.info(_LI("Port %(device)s updated. Details: " "%(device_details)s"), {'device': device, 'device_details': device_details}) self._workers.submit(self._process_added_port, device_details) # remove the port from added ports set, so it doesn't get # reprocessed. self._added_ports.discard(device) def _treat_devices_removed(self): for device in list(self._removed_ports): LOG.info(_LI("Removing port %s"), device) try: self.plugin_rpc.update_device_down(self.context, device, self.agent_id, self._host) except Exception as e: LOG.debug("Removing port failed for device %(device)s: %(e)s", dict(device=device, e=e)) continue self._port_unbound(device, vnic_deleted=True) self.sec_groups_agent.remove_devices_filter([device]) # if the port unbind was successful, remove the port from removed # set, so it won't be reprocessed. self._removed_ports.discard(device) def _process_added_port_event(self, port_name): LOG.info(_LI("Hyper-V VM vNIC added: %s"), port_name) self._added_ports.add(port_name) def _process_removed_port_event(self, port_name): LOG.info(_LI("Hyper-V VM vNIC removed: %s"), port_name) self._removed_ports.add(port_name) def _create_event_listeners(self): event_callback_pairs = [ (self._utils.EVENT_TYPE_CREATE, self._process_added_port_event), (self._utils.EVENT_TYPE_DELETE, self._process_removed_port_event)] for event_type, callback in event_callback_pairs: listener = self._utils.get_vnic_event_listener(event_type) thread = threading.Thread(target=listener, args=(callback,)) thread.start() def daemon_loop(self): self._added_ports = self._utils.get_vnic_ids() self._removed_ports = set() self._create_event_listeners() while True: try: start = time.time() # notify plugin about port deltas if self._added_ports: LOG.debug("Agent loop has new devices!") self._treat_devices_added() if self._removed_ports: LOG.debug("Agent loop has lost devices...") self._treat_devices_removed() if self._nvgre_enabled: self._nvgre_ops.refresh_nvgre_records() self._port_enable_control_metrics() except Exception: LOG.exception(_LE("Error in agent event loop")) # inconsistent cache might cause exceptions. for example, if a # port has been removed, it will be known in the next loop. # using the old switch port can cause exceptions. self._utils.update_cache() # sleep till end of polling interval elapsed = (time.time() - start) if (elapsed < self._polling_interval): time.sleep(self._polling_interval - elapsed) else: LOG.debug("Loop iteration exceeded interval " "(%(polling_interval)s vs. %(elapsed)s)", {'polling_interval': self._polling_interval, 'elapsed': elapsed}) networking-hyperv-2.0.0/hyperv/neutron/neutron_client.py0000664000567000056710000001165612677524354025015 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from neutronclient.v2_0 import client as clientv20 from oslo_config import cfg from oslo_log import log as logging from hyperv.common.i18n import _LW, _LE # noqa from hyperv.neutron import constants CONF = cfg.CONF LOG = logging.getLogger(__name__) neutron_opts = [ cfg.StrOpt('url', default='http://127.0.0.1:9696', help='URL for connecting to neutron'), cfg.IntOpt('url_timeout', default=30, help='timeout value for connecting to neutron in seconds'), cfg.StrOpt('admin_username', help='username for connecting to neutron in admin context'), cfg.StrOpt('admin_password', help='password for connecting to neutron in admin context', secret=True), cfg.StrOpt('admin_tenant_name', help='tenant name for connecting to neutron in admin context'), cfg.StrOpt('admin_auth_url', default='http://localhost:5000/v2.0', help='auth url for connecting to neutron in admin context'), cfg.StrOpt('auth_strategy', default='keystone', help='auth strategy for connecting to neutron in admin context') ] CONF.register_opts(neutron_opts, 'neutron') class NeutronAPIClient(object): def __init__(self): self._init_client() def _init_client(self, token=None): params = { 'endpoint_url': CONF.neutron.url, 'timeout': CONF.neutron.url_timeout, 'insecure': True, 'ca_cert': None, } if token: params['token'] = token params['auth_strategy'] = None else: params['username'] = CONF.neutron.admin_username params['tenant_name'] = CONF.neutron.admin_tenant_name params['password'] = CONF.neutron.admin_password params['auth_url'] = CONF.neutron.admin_auth_url params['auth_strategy'] = CONF.neutron.auth_strategy self._client = clientv20.Client(**params) def get_network_subnets(self, network_id): try: net = self._client.show_network(network_id) return net['network']['subnets'] except Exception as ex: LOG.error(_LE("Could not retrieve network %(network_id)s . Error: " "%(ex)s"), {'network_id': network_id, 'ex': ex}) return [] def get_network_subnet_cidr_and_gateway(self, subnet_id): try: subnet = self._client.show_subnet(subnet_id)['subnet'] return (str(subnet['cidr']), str(subnet['gateway_ip'])) except Exception as ex: LOG.error(_LE("Could not retrieve subnet %(subnet_id)s . Error: " "%(ex)s: "), {'subnet_id': subnet_id, 'ex': ex}) return None, None def get_port_ip_address(self, port_id): try: port = self._client.show_port(port_id) fixed_ips = port['port']['fixed_ips'][0] return fixed_ips['ip_address'] except Exception as ex: LOG.error(_LE("Could not retrieve port %(port_id)s . Error: " "%(ex)s"), {'port_id': port_id, 'ex': ex}) return None def get_tunneling_agents(self): try: agents = self._client.list_agents() tunneling_agents = [ a for a in agents['agents'] if constants.TYPE_NVGRE in a.get('configurations', {}).get('tunnel_types', [])] tunneling_ip_agents = [ a for a in tunneling_agents if a.get('configurations', {}).get('tunneling_ip')] if len(tunneling_ip_agents) < len(tunneling_agents): LOG.warning(_LW('Some agents have NVGRE tunneling enabled, but' ' do not provide tunneling_ip. Ignoring those ' 'agents.')) return dict([(a['host'], a['configurations']['tunneling_ip']) for a in tunneling_ip_agents]) except Exception as ex: LOG.error(_LE("Could not get tunneling agents. Error: %s"), ex) return {} def get_network_ports(self, **kwargs): try: return self._client.list_ports(**kwargs)['ports'] except Exception as ex: LOG.error(_LE("Exception caught: %s"), ex) return [] networking-hyperv-2.0.0/hyperv/neutron/security_groups_driver.py0000775000567000056710000003503012677524354026601 0ustar jenkinsjenkins00000000000000# Copyright 2014 Cloudbase Solutions SRL # 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 eventlet import greenthread import netaddr from neutron.agent import firewall from os_win import exceptions from os_win.utils.network import networkutils from os_win import utilsfactory from oslo_log import log as logging import six from hyperv.common.i18n import _LE, _LI # noqa import threading LOG = logging.getLogger(__name__) INGRESS_DIRECTION = 'ingress' EGRESS_DIRECTION = 'egress' DIRECTION_IP_PREFIX = {'ingress': 'source_ip_prefix', 'egress': 'dest_ip_prefix'} ACL_PROP_MAP = { 'direction': {'ingress': networkutils.NetworkUtils._ACL_DIR_IN, 'egress': networkutils.NetworkUtils._ACL_DIR_OUT}, 'ethertype': {'IPv4': networkutils.NetworkUtils._ACL_TYPE_IPV4, 'IPv6': networkutils.NetworkUtils._ACL_TYPE_IPV6}, 'protocol': {'tcp': networkutils.NetworkUtils._TCP_PROTOCOL, 'udp': networkutils.NetworkUtils._UDP_PROTOCOL, 'icmp': networkutils.NetworkUtils._ICMP_PROTOCOL, 'ipv6-icmp': networkutils.NetworkUtils._ICMPV6_PROTOCOL}, 'action': {'allow': networkutils.NetworkUtils._ACL_ACTION_ALLOW, 'deny': networkutils.NetworkUtils._ACL_ACTION_DENY}, 'default': "ANY", 'address_default': {'IPv4': '0.0.0.0/0', 'IPv6': '::/0'} } class HyperVSecurityGroupsDriverMixin(object): """Security Groups Driver. Security Groups implementation for Hyper-V VMs. """ def __init__(self): self._utils = utilsfactory.get_networkutils() self._sg_gen = SecurityGroupRuleGeneratorR2() self._sec_group_rules = {} self._security_ports = {} self._sg_members = {} self._sg_rule_templates = {} self.cache_lock = threading.Lock() # TODO(claudiub): remove this on the next os-win release. clear_cache = lambda port_id: self._utils._sg_acl_sds.pop(port_id, None) self._utils.clear_port_sg_acls_cache = clear_cache def _select_sg_rules_for_port(self, port, direction): sg_ids = port.get('security_groups', []) port_rules = [] fixed_ips = port.get('fixed_ips', []) for sg_id in sg_ids: for rule in self._sg_rule_templates.get(sg_id, []): if rule['direction'] != direction: continue remote_group_id = rule.get('remote_group_id') if not remote_group_id: grp_rule = rule.copy() grp_rule['security_group_id'] = sg_id port_rules.append(grp_rule) continue ethertype = rule['ethertype'] for ip in self._sg_members[remote_group_id][ethertype]: if ip in fixed_ips: continue ip_rule = rule.copy() direction_ip_prefix = DIRECTION_IP_PREFIX[direction] ip_rule[direction_ip_prefix] = str( netaddr.IPNetwork(ip).cidr) ip_rule['security_group_id'] = sg_id port_rules.append(ip_rule) return port_rules 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 def update_security_group_rules(self, sg_id, sg_rules): LOG.debug("Update rules of security group (%s)", sg_id) with self.cache_lock: self._sg_rule_templates[sg_id] = sg_rules def update_security_group_members(self, sg_id, sg_members): LOG.debug("Update members of security group (%s)", sg_id) with self.cache_lock: self._sg_members[sg_id] = sg_members def _generate_rules(self, ports): newports = {} for port in ports: _rules = [] _rules.extend(self._select_sg_rules_for_port(port, INGRESS_DIRECTION)) _rules.extend(self._select_sg_rules_for_port(port, EGRESS_DIRECTION)) newports[port['id']] = _rules return newports def prepare_port_filter(self, port): LOG.debug('Creating port %s rules', len(port['security_group_rules'])) # newly created port, add default rules. if port['device'] not in self._security_ports: LOG.debug('Creating default reject rules.') self._sec_group_rules[port['id']] = [] def_sg_rules = self._sg_gen.create_default_sg_rules() self._add_sg_port_rules(port['id'], def_sg_rules) # Add provider rules provider_rules = port['security_group_rules'] self._create_port_rules(port['id'], provider_rules) newrules = self._generate_rules([port]) self._create_port_rules(port['id'], newrules[port['id']]) self._security_ports[port['device']] = port self._sec_group_rules[port['id']] = newrules[port['id']] def _create_port_rules(self, port_id, rules): sg_rules = self._sg_gen.create_security_group_rules(rules) old_sg_rules = self._sec_group_rules[port_id] add, rm = self._sg_gen.compute_new_rules_add(old_sg_rules, sg_rules) self._add_sg_port_rules(port_id, list(set(add))) self._remove_sg_port_rules(port_id, list(set(rm))) def _remove_port_rules(self, port_id, rules): sg_rules = self._sg_gen.create_security_group_rules(rules) self._remove_sg_port_rules(port_id, list(set(sg_rules))) def _add_sg_port_rules(self, port_id, sg_rules): if not sg_rules: return old_sg_rules = self._sec_group_rules[port_id] # yielding to other threads that must run (like state reporting) greenthread.sleep() try: self._utils.create_security_rules(port_id, sg_rules) old_sg_rules.extend(sg_rules) except exceptions.NotFound: # port no longer exists. self._sec_group_rules.pop(port_id, None) raise except Exception: LOG.exception(_LE('Exception encountered while adding rules for ' 'port: %s'), port_id) raise def _remove_sg_port_rules(self, port_id, sg_rules): if not sg_rules: return old_sg_rules = self._sec_group_rules[port_id] try: self._utils.remove_security_rules(port_id, sg_rules) for rule in sg_rules: if rule in old_sg_rules: old_sg_rules.remove(rule) except exceptions.NotFound: # port no longer exists. self._sec_group_rules.pop(port_id, None) raise except Exception: LOG.exception(_LE('Exception encountered while removing rules for ' 'port: %s'), port_id) raise def apply_port_filter(self, port): LOG.info(_LI('Aplying port filter.')) def update_port_filter(self, port): LOG.info(_LI('Updating port rules.')) if port['device'] not in self._security_ports: LOG.info(_LI("Device %(port)s not yet added."), {'port': port['id']}) return old_port = self._security_ports[port['device']] old_provider_rules = old_port['security_group_rules'] added_provider_rules = port['security_group_rules'] # Generate the rules added_rules = self._generate_rules([port]) # Consider added provider rules (if any) new_rules = [r for r in added_provider_rules if r not in old_provider_rules] # Build new rules to add new_rules.extend([r for r in added_rules[port['id']] if r not in self._sec_group_rules[port['id']]]) # Remove non provider rules remove_rules = [r for r in self._sec_group_rules[port['id']] if r not in added_rules[port['id']]] # Remove for non provider rules remove_rules.extend([r for r in old_provider_rules if r not in added_provider_rules]) LOG.info(_("Creating %(new)s new rules, removing %(old)s " "old rules."), {'new': len(new_rules), 'old': len(remove_rules)}) self._create_port_rules(port['id'], new_rules) self._remove_port_rules(old_port['id'], remove_rules) self._security_ports[port['device']] = port self._sec_group_rules[port['id']] = added_rules[port['id']] def remove_port_filter(self, port): LOG.info(_LI('Removing port filter')) self._security_ports.pop(port['device'], None) self._sec_group_rules.pop(port['id'], None) self._utils.clear_port_sg_acls_cache(port['id']) def security_group_updated(self, action_type, sec_group_ids, device_id=None): pass @property def ports(self): return self._security_ports class SecurityGroupRuleGenerator(object): def create_security_group_rules(self, rules): security_group_rules = [] for rule in rules: security_group_rules.extend(self.create_security_group_rule(rule)) return security_group_rules def create_security_group_rule(self, rule): # TODO(claudiub): implement pass def _get_rule_remote_address(self, rule): if rule['direction'] == 'ingress': ip_prefix = 'source_ip_prefix' else: ip_prefix = 'dest_ip_prefix' if ip_prefix in rule: return rule[ip_prefix] return ACL_PROP_MAP['address_default'][rule['ethertype']] class SecurityGroupRuleGeneratorR2(SecurityGroupRuleGenerator): def create_security_group_rule(self, rule): local_port = self._get_rule_port_range(rule) direction = ACL_PROP_MAP['direction'][rule['direction']] remote_address = self._get_rule_remote_address(rule) remote_address = remote_address.split('/128', 1)[0] protocol = self._get_rule_protocol(rule) if protocol == ACL_PROP_MAP['default']: # ANY protocols must be split up, to make stateful rules. protocols = list(ACL_PROP_MAP['protocol'].values()) else: protocols = [protocol] sg_rules = [SecurityGroupRuleR2(direction=direction, local_port=local_port, protocol=proto, remote_addr=remote_address) for proto in protocols] return sg_rules def create_default_sg_rules(self): ip_type_pairs = [(ACL_PROP_MAP['ethertype'][ip], ACL_PROP_MAP['address_default'][ip]) for ip in six.iterkeys(ACL_PROP_MAP['ethertype'])] action = ACL_PROP_MAP['action']['deny'] port = ACL_PROP_MAP['default'] sg_rules = [] for direction in ACL_PROP_MAP['direction'].values(): for protocol in ACL_PROP_MAP['protocol'].values(): for acl_type, address in ip_type_pairs: sg_rules.append(SecurityGroupRuleR2(direction=direction, local_port=port, protocol=protocol, remote_addr=address, action=action)) return sg_rules def compute_new_rules_add(self, old_rules, new_rules): add_rules = [r for r in new_rules if r not in old_rules] return add_rules, [] def _get_rule_port_range(self, rule): if 'port_range_min' in rule and 'port_range_max' in rule: return '%s-%s' % (rule['port_range_min'], rule['port_range_max']) return ACL_PROP_MAP['default'] def _get_rule_protocol(self, rule): protocol = self._get_rule_prop_or_default(rule, 'protocol') if protocol in six.iterkeys(ACL_PROP_MAP['protocol']): return ACL_PROP_MAP['protocol'][protocol] return protocol def _get_rule_prop_or_default(self, rule, prop): if prop in rule: return rule[prop] return ACL_PROP_MAP['default'] class SecurityGroupRuleBase(object): _FIELDS = [] def __eq__(self, obj): for f in self._FIELDS: if not hasattr(obj, f) or getattr(obj, f) != getattr(self, f): return False return True def __str__(self): return str(self.to_dict()) def __repr__(self): return str(self) def to_dict(self): return dict((field, getattr(self, field)) for field in self._FIELDS) class SecurityGroupRuleR2(SecurityGroupRuleBase): _FIELDS = ["Direction", "Action", "LocalPort", "Protocol", "RemoteIPAddress", "Stateful", "IdleSessionTimeout"] IdleSessionTimeout = 0 Weight = 65500 def __init__(self, direction, local_port, protocol, remote_addr, action=ACL_PROP_MAP['action']['allow']): is_not_icmp = protocol not in [ACL_PROP_MAP['protocol']['icmp'], ACL_PROP_MAP['protocol']['ipv6-icmp']] self.Direction = direction self.Action = action self.LocalPort = str(local_port) if is_not_icmp else '' self.Protocol = protocol self.RemoteIPAddress = remote_addr self.Stateful = (is_not_icmp and action is not ACL_PROP_MAP['action']['deny']) self._cached_hash = hash((direction, action, self.LocalPort, protocol, remote_addr)) def __lt__(self, obj): return self.Protocol > obj.Protocol def __hash__(self): return self._cached_hash class HyperVSecurityGroupsDriver(HyperVSecurityGroupsDriverMixin, firewall.FirewallDriver): pass networking-hyperv-2.0.0/hyperv/neutron/nvgre_ops.py0000664000567000056710000002134212677524354023760 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from os_win import utilsfactory from oslo_config import cfg from oslo_log import log as logging import six import uuid from hyperv.common.i18n import _LI, _LW, _LE # noqa from hyperv.neutron import constants from hyperv.neutron import hyperv_agent_notifier from hyperv.neutron import neutron_client CONF = cfg.CONF CONF.import_group('AGENT', 'hyperv.neutron.config') CONF.import_group('NVGRE', 'hyperv.neutron.config') LOG = logging.getLogger(__name__) class HyperVNvgreOps(object): def __init__(self, physical_networks): self.topic = constants.AGENT_TOPIC self._vswitch_ips = {} self._tunneling_agents = {} self._nvgre_ports = [] self._network_vsids = {} self._hyperv_utils = utilsfactory.get_networkutils() self._nvgre_utils = utilsfactory.get_nvgreutils() self._n_client = neutron_client.NeutronAPIClient() self._init_nvgre(physical_networks) def init_notifier(self, context, rpc_client): self.context = context self._notifier = hyperv_agent_notifier.AgentNotifierApi( self.topic, rpc_client) def _init_nvgre(self, physical_networks): for network in physical_networks: LOG.info(_LI("Adding provider route and address for network: %s"), network) self._nvgre_utils.create_provider_route(network) self._nvgre_utils.create_provider_address( network, CONF.NVGRE.provider_vlan_id) ip_addr, length = self._nvgre_utils.get_network_iface_ip(network) self._vswitch_ips[network] = ip_addr def _refresh_tunneling_agents(self): self._tunneling_agents.update(self._n_client.get_tunneling_agents()) def lookup_update(self, kwargs): lookup_ip = kwargs.get('lookup_ip') lookup_details = kwargs.get('lookup_details') LOG.info(_LI("Lookup Received: %(lookup_ip)s, %(lookup_details)s"), {'lookup_ip': lookup_ip, 'lookup_details': lookup_details}) if not lookup_ip or not lookup_details: return self._register_lookup_record(lookup_ip, lookup_details['customer_addr'], lookup_details['mac_addr'], lookup_details['customer_vsid']) def tunnel_update(self, context, tunnel_ip, tunnel_type): if tunnel_type != constants.TYPE_NVGRE: return self._notifier.tunnel_update(context, CONF.NVGRE.provider_tunnel_ip, tunnel_type) def _register_lookup_record(self, prov_addr, cust_addr, mac_addr, vsid): LOG.info(_LI('Creating LookupRecord: VSID: %(vsid)s MAC: %(mac_addr)s ' 'Customer IP: %(cust_addr)s Provider IP: %(prov_addr)s'), dict(vsid=vsid, mac_addr=mac_addr, cust_addr=cust_addr, prov_addr=prov_addr)) self._nvgre_utils.create_lookup_record( prov_addr, cust_addr, mac_addr, vsid) def bind_nvgre_port(self, segmentation_id, network_name, port_id): mac_addr = self._hyperv_utils.get_vnic_mac_address(port_id) provider_addr = self._nvgre_utils.get_network_iface_ip(network_name)[0] customer_addr = self._n_client.get_port_ip_address(port_id) if not provider_addr or not customer_addr: LOG.warning(_LW('Cannot bind NVGRE port. Could not determine ' 'provider address (%(prov_addr)s) or customer ' 'address (%(cust_addr)s).'), {'prov_addr': provider_addr, 'cust_addr': customer_addr}) return LOG.info(_LI('Binding VirtualSubnetID %(segmentation_id)s ' 'to switch port %(port_id)s'), dict(segmentation_id=segmentation_id, port_id=port_id)) self._hyperv_utils.set_vswitch_port_vsid(segmentation_id, port_id) # normal lookup record. self._register_lookup_record( provider_addr, customer_addr, mac_addr, segmentation_id) # lookup record for dhcp requests. self._register_lookup_record( self._vswitch_ips[network_name], constants.IPV4_DEFAULT, mac_addr, segmentation_id) LOG.info('Fanning out LookupRecord...') self._notifier.lookup_update(self.context, provider_addr, {'customer_addr': customer_addr, 'mac_addr': mac_addr, 'customer_vsid': segmentation_id}) def bind_nvgre_network(self, segmentation_id, net_uuid, vswitch_name): subnets = self._n_client.get_network_subnets(net_uuid) if len(subnets) > 1: LOG.warning(_LW("Multiple subnets in the same network is not " "supported.")) subnet = subnets[0] try: cidr, gw = self._n_client.get_network_subnet_cidr_and_gateway( subnet) cust_route_string = vswitch_name + cidr + str(segmentation_id) rdid_uuid = str(uuid.uuid5(uuid.NAMESPACE_X500, cust_route_string)) self._create_customer_routes(segmentation_id, cidr, gw, rdid_uuid) except Exception as ex: LOG.error(_LE("Exception caught: %s"), ex) self._network_vsids[net_uuid] = segmentation_id self.refresh_nvgre_records(network_id=net_uuid) self._notifier.tunnel_update( self.context, CONF.NVGRE.provider_tunnel_ip, segmentation_id) def _create_customer_routes(self, segmentation_id, cidr, gw, rdid_uuid): self._nvgre_utils.clear_customer_routes(segmentation_id) # create cidr -> 0.0.0.0/0 customer route self._nvgre_utils.create_customer_route( segmentation_id, cidr, constants.IPV4_DEFAULT, rdid_uuid) if not gw: LOG.info(_LI('Subnet does not have gateway configured. ' 'Skipping.')) elif gw.split('.')[-1] == '1': LOG.error(_LE('Subnet has unsupported gateway IP ending in 1: ' '%s. Any other gateway IP is supported.'), gw) else: # create 0.0.0.0/0 -> gateway customer route self._nvgre_utils.create_customer_route( segmentation_id, '%s/0' % constants.IPV4_DEFAULT, gw, rdid_uuid) # create metadata address -> gateway customer route metadata_addr = '%s/32' % CONF.AGENT.neutron_metadata_address self._nvgre_utils.create_customer_route( segmentation_id, metadata_addr, gw, rdid_uuid) def refresh_nvgre_records(self, **kwargs): self._refresh_tunneling_agents() ports = self._n_client.get_network_ports(**kwargs) # process ports that were not processed yet. # process ports that are bound to tunneling_agents. ports = [p for p in ports if p['id'] not in self._nvgre_ports and p['binding:host_id'] in self._tunneling_agents and p['network_id'] in six.iterkeys(self._network_vsids)] for port in ports: tunneling_ip = self._tunneling_agents[port['binding:host_id']] customer_addr = port['fixed_ips'][0]['ip_address'] mac_addr = port['mac_address'].replace(':', '') segmentation_id = self._network_vsids[port['network_id']] try: self._register_lookup_record( tunneling_ip, customer_addr, mac_addr, segmentation_id) self._nvgre_ports.append(port['id']) except Exception as ex: LOG.error(_LE("Exception while adding lookup_record: %(ex)s. " "VSID: %(vsid)s MAC: %(mac_address)s Customer " "IP:%(cust_addr)s Provider IP: %(prov_addr)s"), dict(ex=ex, vsid=segmentation_id, mac_address=mac_addr, cust_addr=customer_addr, prov_addr=tunneling_ip)) networking-hyperv-2.0.0/hyperv/neutron/l2_agent.py0000775000567000056710000001301612677524354023453 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions Srl # # 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 platform import sys from neutron.agent.common import config from neutron.agent import rpc as agent_rpc from neutron.agent import securitygroups_rpc as sg_rpc from neutron.common import config as common_config from neutron.common import constants as n_const from neutron.common import rpc as n_rpc from neutron.common import topics from neutron import context from oslo_config import cfg from oslo_log import log as logging import oslo_messaging from oslo_service import loopingcall from hyperv.common.i18n import _, _LE, _LI # noqa from hyperv.neutron import constants as h_const from hyperv.neutron import hyperv_neutron_agent LOG = logging.getLogger(__name__) CONF = cfg.CONF class HyperVSecurityAgent(sg_rpc.SecurityGroupAgentRpc): def __init__(self, context, plugin_rpc): super(HyperVSecurityAgent, self).__init__(context, plugin_rpc) if sg_rpc.is_firewall_enabled(): self._setup_rpc() @property def use_enhanced_rpc(self): return True def _setup_rpc(self): self.topic = topics.AGENT self.endpoints = [HyperVSecurityCallbackMixin(self)] consumers = [[topics.SECURITY_GROUP, topics.UPDATE]] self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers) class HyperVSecurityCallbackMixin(sg_rpc.SecurityGroupAgentRpcCallbackMixin): target = oslo_messaging.Target(version='1.3') def __init__(self, sg_agent): super(HyperVSecurityCallbackMixin, self).__init__() self.sg_agent = sg_agent class HyperVNeutronAgent(hyperv_neutron_agent.HyperVNeutronAgentMixin): # Set RPC API version to 1.1 by default. target = oslo_messaging.Target(version='1.1') def __init__(self): self._setup_rpc() super(HyperVNeutronAgent, self).__init__(cfg.CONF) self._set_agent_state() def _set_agent_state(self): configurations = self._get_agent_configurations() self.agent_state = { 'binary': 'neutron-hyperv-agent', 'host': CONF.host, 'configurations': configurations, 'agent_type': h_const.AGENT_TYPE_HYPERV, 'topic': n_const.L2_AGENT_TOPIC, 'start_flag': True} def _get_agent_configurations(self): configurations = {'vswitch_mappings': self._physical_network_mappings} if CONF.NVGRE.enable_support: configurations['arp_responder_enabled'] = False configurations['tunneling_ip'] = CONF.NVGRE.provider_tunnel_ip configurations['devices'] = 1 configurations['l2_population'] = False configurations['tunnel_types'] = [h_const.TYPE_NVGRE] configurations['enable_distributed_routing'] = False configurations['bridge_mappings'] = {} return configurations def _report_state(self): try: self.state_rpc.report_state(self.context, self.agent_state) self.agent_state.pop('start_flag', None) except Exception: LOG.exception(_LE("Failed reporting state!")) def _setup_rpc(self): self.agent_id = 'hyperv_%s' % platform.node() self.topic = topics.AGENT self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN) # RPC network init self.context = context.get_admin_context_without_session() self.sec_groups_agent = HyperVSecurityAgent(self.context, self.sg_plugin_rpc) self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) # Handle updates from service self.endpoints = [self] # Define the listening consumers for the agent consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.PORT, topics.DELETE]] if CONF.NVGRE.enable_support: consumers.append([h_const.TUNNEL, topics.UPDATE]) consumers.append([h_const.LOOKUP, h_const.UPDATE]) self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers) self.client = n_rpc.get_client(self.target) report_interval = CONF.AGENT.report_interval if report_interval: heartbeat = loopingcall.FixedIntervalLoopingCall( self._report_state) heartbeat.start(interval=report_interval) def main(): config.register_agent_state_opts_helper(cfg.CONF) common_config.init(sys.argv[1:]) config.setup_logging() hyperv_agent = HyperVNeutronAgent() # Start everything. LOG.info(_LI("Agent initialized successfully, now running... ")) hyperv_agent.daemon_loop() networking-hyperv-2.0.0/hyperv/neutron/constants.py0000664000567000056710000000203612677524354023771 0ustar jenkinsjenkins00000000000000# Copyright 2013 Cloudbase Solutions SRL # 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. # Topic for tunnel notifications between the plugin and agent AGENT_TOPIC = 'q-agent-notifier' AGENT_TYPE_HYPERV = 'HyperV agent' VIF_TYPE_HYPERV = 'hyperv' TUNNEL = 'tunnel' LOOKUP = 'lookup' UPDATE = 'update' # Special vlan_id value in ovs_vlan_allocations table indicating flat network FLAT_VLAN_ID = -1 TYPE_FLAT = 'flat' TYPE_LOCAL = 'local' TYPE_VLAN = 'vlan' TYPE_NVGRE = 'gre' IPV4_DEFAULT = '0.0.0.0' networking-hyperv-2.0.0/hyperv/neutron/ml2/0000775000567000056710000000000012677524665022101 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/neutron/ml2/__init__.py0000664000567000056710000000000012677524354024173 0ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/neutron/ml2/mech_hyperv.py0000664000567000056710000000400612677524354024757 0ustar jenkinsjenkins00000000000000# Copyright (c) 2015 Cloudbase Solutions Srl # 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. import re from neutron.extensions import portbindings from neutron.plugins.ml2.drivers import mech_agent from hyperv.neutron import constants class HypervMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase): """Attach to networks using Hyper-V L2 Agent. The HypervMechanismDriver integrates the Ml2 Plugin with the Hyperv L2 Agent. Port binding with this driver requires the Hyper-V agent to be running on the port's host, and that agent to have connectivity to at least one segment of the port's network. """ def __init__(self): super(HypervMechanismDriver, self).__init__( constants.AGENT_TYPE_HYPERV, constants.VIF_TYPE_HYPERV, {portbindings.CAP_PORT_FILTER: False}) def get_allowed_network_types(self, agent=None): network_types = [constants.TYPE_LOCAL, constants.TYPE_FLAT, constants.TYPE_VLAN] if agent is not None: tunnel_types = agent.get('configurations', {}).get('tunnel_types') if tunnel_types: network_types.extend(tunnel_types) return network_types def get_mappings(self, agent): return agent['configurations'].get('vswitch_mappings', {}) def physnet_in_mappings(self, physnet, mappings): return any(re.match(pattern, physnet) for pattern in mappings) networking-hyperv-2.0.0/hyperv/neutron/ml2/README0000664000567000056710000000131612677524354022755 0ustar jenkinsjenkins00000000000000Hyper-V Neutron Agent and ML2 Mechanism Driver for ML2 Plugin ============================================================= In order to properly use the Hyper-V Neutron Agent, neutron will have to use the Ml2Plugin. This can be done by setting the ``core_plugin`` field in ``neutron.conf`` to: :: core_plugin = neutron.plugins.ml2.plugin.Ml2Plugin Additionally, the ML2 Plugin must be configured to use the Hyper-V Mechanism Driver, by adding it to the ``mechanism_drivers`` field in ``ml2_conf.ini``: :: [ml2] mechanism_drivers = openvswitch,hyperv # any other mechanism_drivers can be added to the list. Currently, the mechanism driver supports the following network types: local, flat, VLAN. networking-hyperv-2.0.0/hyperv/neutron/config.py0000664000567000056710000000646312677524354023232 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions Srl # # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_config import cfg from hyperv.common.i18n import _ HYPERV_AGENT_OPTS = [ cfg.ListOpt( 'physical_network_vswitch_mappings', default=[], help=_('List of : ' 'where the physical networks can be expressed with ' 'wildcards, e.g.: ."*:external"')), cfg.StrOpt( 'local_network_vswitch', default='private', help=_('Private vswitch name used for local networks')), cfg.IntOpt('polling_interval', default=2, help=_("The number of seconds the agent will wait between " "polling for local device changes.")), cfg.IntOpt('worker_count', default=10, help=_("The number of worker threads allowed to run in " "parallel to process port binding.")), cfg.IntOpt('worker_retry', default=3, help=_("The number of times worker process will retry " "port binding.")), cfg.BoolOpt('enable_metrics_collection', default=False, help=_('Enables metrics collections for switch ports by using ' 'Hyper-V\'s metric APIs. Collected data can by ' 'retrieved by other apps and services, e.g.: ' 'Ceilometer. Requires Hyper-V / Windows Server 2012 ' 'and above')), cfg.IntOpt('metrics_max_retries', default=100, help=_('Specifies the maximum number of retries to enable ' 'Hyper-V\'s port metrics collection. The agent will try ' 'to enable the feature once every polling_interval ' 'period for at most metrics_max_retries or until it ' 'succeedes.')), cfg.StrOpt('neutron_metadata_address', default='169.254.169.254', help=_('Specifies the address which will serve the metadata for' ' the instance.')), ] NVGRE_OPTS = [ cfg.BoolOpt('enable_support', default=False, help=_('Enables Hyper-V NVGRE. ' 'Requires Windows Server 2012 or above.')), cfg.IntOpt('provider_vlan_id', default=0, help=_('Specifies the VLAN ID of the physical network, required' ' for setting the NVGRE Provider Address.')), cfg.StrOpt('provider_tunnel_ip', default=None, help=_('Specifies the tunnel IP which will be used and ' 'reported by this host for NVGRE networks.')), ] cfg.CONF.register_opts(HYPERV_AGENT_OPTS, "AGENT") cfg.CONF.register_opts(NVGRE_OPTS, "NVGRE") networking-hyperv-2.0.0/hyperv/neutron/hyperv_agent_notifier.py0000664000567000056710000000505512677524354026353 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. from oslo_log import log as logging from hyperv.neutron import constants LOG = logging.getLogger(__name__) def get_topic_name(prefix, table, operation): """Create a topic name. The topic name needs to be synced between the agents. The agent will send a fanout message to all of the listening agents so that the agents in turn can perform their updates accordingly. :param prefix: Common prefix for the agent message queues. :param table: The table in question (TUNNEL, LOOKUP). :param operation: The operation that invokes notification (UPDATE) :returns: The topic name. """ return '%s-%s-%s' % (prefix, table, operation) class AgentNotifierApi(object): """Agent side of the OpenVSwitch rpc API.""" def __init__(self, topic, client): self._client = client self.topic_tunnel_update = get_topic_name(topic, constants.TUNNEL, constants.UPDATE) self.topic_lookup_update = get_topic_name(topic, constants.LOOKUP, constants.UPDATE) def _fanout_cast(self, context, topic, method, **info): cctxt = self._client.prepare(topic=topic, fanout=True) cctxt.cast(context, method, **info) def tunnel_update(self, context, tunnel_ip, tunnel_type): self._fanout_cast(context, self.topic_tunnel_update, 'tunnel_update', tunnel_ip=tunnel_ip, tunnel_type=tunnel_type) def lookup_update(self, context, lookup_ip, lookup_details): self._fanout_cast(context, self.topic_lookup_update, 'lookup_update', lookup_ip=lookup_ip, lookup_details=lookup_details) networking-hyperv-2.0.0/hyperv/__init__.py0000664000567000056710000000000012677524354022007 0ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/common/0000775000567000056710000000000012677524665021205 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/common/__init__.py0000664000567000056710000000000012677524354023277 0ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/common/i18n.py0000664000567000056710000000206212677524354022331 0ustar jenkinsjenkins00000000000000# 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 _translators = oslo_i18n.TranslatorFactory(domain='neutron') # The primary translation function using the well-known name "_" _ = _translators.primary # Translators for log levels. # # The abbreviated names are meant to reflect the usual use of a short # name like '_'. The "L" is for "log" and the other letter comes from # the level. _LI = _translators.log_info _LW = _translators.log_warning _LE = _translators.log_error _LC = _translators.log_critical networking-hyperv-2.0.0/hyperv/tests/0000775000567000056710000000000012677524665021057 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/tests/__init__.py0000664000567000056710000000000012677524354023151 0ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/tests/base.py0000664000567000056710000001177112677524354022345 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions Srl # Copyright 2010-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. """Base test case for tests that do not rely on Tempest.""" import contextlib import logging as std_logging import os import os.path import traceback import eventlet.timeout import fixtures import mock from oslo_config import cfg from oslo_utils import strutils import six import testtools CONF = cfg.CONF LOG_FORMAT = "%(asctime)s %(levelname)8s [%(name)s] %(message)s" def bool_from_env(key, strict=False, default=False): value = os.environ.get(key) return strutils.bool_from_string(value, strict=strict, default=default) class BaseTestCase(testtools.TestCase): def setUp(self): super(BaseTestCase, self).setUp() self.addCleanup(CONF.reset) self.addCleanup(mock.patch.stopall) if bool_from_env('OS_DEBUG'): _level = std_logging.DEBUG else: _level = std_logging.INFO capture_logs = bool_from_env('OS_LOG_CAPTURE') if not capture_logs: std_logging.basicConfig(format=LOG_FORMAT, level=_level) self.log_fixture = self.useFixture( fixtures.FakeLogger( format=LOG_FORMAT, level=_level, nuke_handlers=capture_logs, )) test_timeout = int(os.environ.get('OS_TEST_TIMEOUT', 0)) if test_timeout == -1: test_timeout = 0 if test_timeout > 0: self.useFixture(fixtures.Timeout(test_timeout, gentle=True)) if bool_from_env('OS_STDOUT_CAPTURE'): stdout = self.useFixture(fixtures.StringStream('stdout')).stream self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout)) if bool_from_env('OS_STDERR_CAPTURE'): stderr = self.useFixture(fixtures.StringStream('stderr')).stream self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr)) self.addOnException(self.check_for_systemexit) def check_for_systemexit(self, exc_info): if isinstance(exc_info[1], SystemExit): self.fail("A SystemExit was raised during the test. %s" % traceback.format_exception(*exc_info)) @contextlib.contextmanager def assert_max_execution_time(self, max_execution_time=5): with eventlet.timeout.Timeout(max_execution_time, False): yield return self.fail('Execution of this test timed out') def assertOrderedEqual(self, expected, actual): expect_val = self.sort_dict_lists(expected) actual_val = self.sort_dict_lists(actual) self.assertEqual(expect_val, actual_val) def sort_dict_lists(self, dic): for key, value in six.iteritems(dic): if isinstance(value, list): dic[key] = sorted(value) elif isinstance(value, dict): dic[key] = self.sort_dict_lists(value) return dic def assertDictSupersetOf(self, expected_subset, actual_superset): """Checks that actual dict contains the expected dict. After checking that the arguments are of the right type, this checks that each item in expected_subset is in, and matches, what is in actual_superset. Separate tests are done, so that detailed info can be reported upon failure. """ if not isinstance(expected_subset, dict): self.fail("expected_subset (%s) is not an instance of dict" % type(expected_subset)) if not isinstance(actual_superset, dict): self.fail("actual_superset (%s) is not an instance of dict" % type(actual_superset)) for k, v in six.iteritems(expected_subset): self.assertIn(k, actual_superset) self.assertEqual(v, actual_superset[k], "Key %(key)s expected: %(exp)r, actual %(act)r" % {'key': k, 'exp': v, 'act': actual_superset[k]}) def config(self, **kw): """Override some configuration values. The keyword arguments are the names of configuration options to override and their values. If a group argument is supplied, the overrides are applied to the specified configuration option group. All overrides are automatically cleared at the end of the current test by the fixtures cleanup process. """ group = kw.pop('group', None) for k, v in six.iteritems(kw): CONF.set_override(k, v, group) networking-hyperv-2.0.0/hyperv/tests/unit/0000775000567000056710000000000012677524665022036 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/tests/unit/neutron/0000775000567000056710000000000012677524665023530 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_l2_agent.py0000664000567000056710000001561212677524354026634 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions Srl # 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. """ Unit tests for Windows Hyper-V L2 agent. """ import platform import sys import mock from neutron.common import constants as n_const from neutron.common import topics from oslo_config import cfg from hyperv.neutron import constants from hyperv.neutron import l2_agent from hyperv.tests import base CONF = cfg.CONF class TestHyperVSecurityAgent(base.BaseTestCase): @mock.patch.object(l2_agent.HyperVSecurityAgent, '__init__', lambda *args, **kwargs: None) def setUp(self): super(TestHyperVSecurityAgent, self).setUp() self.agent = l2_agent.HyperVSecurityAgent() @mock.patch.object(l2_agent, 'HyperVSecurityCallbackMixin') @mock.patch.object(l2_agent.agent_rpc, 'create_consumers') def test_setup_rpc(self, mock_create_consumers, mock_HyperVSecurity): self.agent._setup_rpc() self.assertEqual(topics.AGENT, self.agent.topic) self.assertEqual([mock_HyperVSecurity.return_value], self.agent.endpoints) self.assertEqual(mock_create_consumers.return_value, self.agent.connection) mock_create_consumers.assert_called_once_with( self.agent.endpoints, self.agent.topic, [[topics.SECURITY_GROUP, topics.UPDATE]]) class TestHyperVNeutronAgent(base.BaseTestCase): @mock.patch.object(l2_agent.HyperVNeutronAgent, '__init__', lambda *args, **kwargs: None) def setUp(self): super(TestHyperVNeutronAgent, self).setUp() self.agent = l2_agent.HyperVNeutronAgent() self.agent.context = mock.sentinel.context self.agent._physical_network_mappings = {} @mock.patch.object(l2_agent.HyperVNeutronAgent, '_get_agent_configurations') def test_set_agent_state(self, mock_get_config): mock_get_config.return_value = {mock.sentinel.key: mock.sentinel.val} self.agent._set_agent_state() expected = { 'binary': 'neutron-hyperv-agent', 'host': CONF.host, 'configurations': {mock.sentinel.key: mock.sentinel.val}, 'agent_type': constants.AGENT_TYPE_HYPERV, 'topic': n_const.L2_AGENT_TOPIC, 'start_flag': True } self.assertEqual(expected, self.agent.agent_state) def test_get_agent_configurations(self): actual = self.agent._get_agent_configurations() self.assertEqual(self.agent._physical_network_mappings, actual['vswitch_mappings']) self.assertNotIn('tunnel_types', actual) self.assertNotIn('tunneling_ip', actual) def test_get_agent_configurations_nvgre(self): self.config(enable_support=True, group='NVGRE') self.config(provider_tunnel_ip=mock.sentinel.tunneling_ip, group='NVGRE') actual = self.agent._get_agent_configurations() self.assertEqual(self.agent._physical_network_mappings, actual['vswitch_mappings']) self.assertEqual([constants.TYPE_NVGRE], actual['tunnel_types']) self.assertEqual(mock.sentinel.tunneling_ip, actual['tunneling_ip']) def test_report_state(self): self.agent.agent_state = {'start_flag': True} self.agent.state_rpc = mock.MagicMock() self.agent._report_state() self.assertNotIn('start_flag', self.agent.agent_state) def test_report_state_exception(self): self.agent.agent_state = {'start_flag': True} self.agent.state_rpc = mock.MagicMock() self.agent.state_rpc.report_state.side_effect = Exception self.agent._report_state() self.agent.state_rpc.report_state.assert_called_once_with( self.agent.context, {'start_flag': True}) self.assertTrue(self.agent.agent_state['start_flag']) @mock.patch.object(l2_agent.loopingcall, 'FixedIntervalLoopingCall') @mock.patch.object(l2_agent.n_rpc, 'get_client') @mock.patch.object(l2_agent, 'HyperVSecurityAgent') @mock.patch.object(l2_agent.sg_rpc, 'SecurityGroupServerRpcApi') @mock.patch.object(l2_agent, 'agent_rpc') @mock.patch.object(l2_agent, 'CONF') def test_setup_rpc(self, mock_CONF, mock_agent_rpc, mock_SGRpcApi, mock_HyperVSecurityAgent, mock_get_client, mock_LoopingCall): mock_CONF.NVGRE.enable_support = True mock_CONF.AGENT.report_interval = mock.sentinel.report_interval self.agent._setup_rpc() self.assertEqual('hyperv_%s' % platform.node(), self.agent.agent_id) self.assertEqual(topics.AGENT, self.agent.topic) self.assertEqual(mock_agent_rpc.PluginApi.return_value, self.agent.plugin_rpc) self.assertEqual(mock_HyperVSecurityAgent.return_value, self.agent.sec_groups_agent) self.assertEqual([self.agent], self.agent.endpoints) self.assertEqual(mock_agent_rpc.create_consumers.return_value, self.agent.connection) self.assertEqual(mock_get_client.return_value, self.agent.client) mock_HyperVSecurityAgent.assert_called_once_with( self.agent.context, self.agent.sg_plugin_rpc) consumers = [[topics.PORT, topics.UPDATE], [topics.NETWORK, topics.DELETE], [topics.PORT, topics.DELETE], [constants.TUNNEL, topics.UPDATE], [constants.LOOKUP, constants.UPDATE]] mock_agent_rpc.create_consumers.assert_called_once_with( self.agent.endpoints, self.agent.topic, consumers) mock_LoopingCall.return_value.start.assert_called_once_with( interval=mock.sentinel.report_interval) class TestMain(base.BaseTestCase): @mock.patch.object(l2_agent, 'HyperVNeutronAgent') @mock.patch.object(l2_agent, 'common_config') @mock.patch.object(l2_agent, 'config') def test_main(self, mock_config, mock_common_config, mock_HyperVAgent): l2_agent.main() mock_config.register_agent_state_opts_helper.assert_called_once_with( CONF) mock_common_config.init.assert_called_once_with(sys.argv[1:]) mock_config.setup_logging.assert_called_once_with() mock_HyperVAgent.assert_called_once_with() mock_HyperVAgent.return_value.daemon_loop.assert_called_once_with() networking-hyperv-2.0.0/hyperv/tests/unit/neutron/__init__.py0000664000567000056710000000000012677524354025622 0ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_nvgre_ops.py0000664000567000056710000002267212677524354027147 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # 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. """ Unit tests for Windows Hyper-V NVGRE driver. """ import mock from os_win import utilsfactory from oslo_config import cfg from hyperv.neutron import constants from hyperv.neutron import nvgre_ops from hyperv.tests import base CONF = cfg.CONF class TestHyperVNvgreOps(base.BaseTestCase): FAKE_MAC_ADDR = 'fa:ke:ma:ca:dd:re:ss' FAKE_CIDR = '10.0.0.0/24' FAKE_VSWITCH_NAME = 'fake_vswitch' def setUp(self): super(TestHyperVNvgreOps, self).setUp() utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class') utilsfactory_patcher.start() self.addCleanup(utilsfactory_patcher.stop) self.context = 'context' self.ops = nvgre_ops.HyperVNvgreOps([]) self.ops._vswitch_ips[mock.sentinel.network_name] = ( mock.sentinel.ip_addr) self.ops.context = self.context self.ops._notifier = mock.MagicMock() self.ops._hyperv_utils = mock.MagicMock() self.ops._nvgre_utils = mock.MagicMock() self.ops._n_client = mock.MagicMock() self.ops._db = mock.MagicMock() def test_refresh_tunneling_agents(self): self.ops._n_client.get_tunneling_agents.return_value = { mock.sentinel.host: mock.sentinel.host_ip } self.ops._refresh_tunneling_agents() self.assertEqual(mock.sentinel.host_ip, self.ops._tunneling_agents[mock.sentinel.host]) @mock.patch.object(nvgre_ops.HyperVNvgreOps, '_register_lookup_record') def test_lookup_update(self, mock_register_record): args = {'lookup_ip': mock.sentinel.lookup_ip, 'lookup_details': { 'customer_addr': mock.sentinel.customer_addr, 'mac_addr': mock.sentinel.mac_addr, 'customer_vsid': mock.sentinel.vsid} } self.ops.lookup_update(args) mock_register_record.assert_called_once_with( mock.sentinel.lookup_ip, mock.sentinel.customer_addr, mock.sentinel.mac_addr, mock.sentinel.vsid) @mock.patch.object(nvgre_ops.HyperVNvgreOps, '_register_lookup_record') def test_lookup_update_no_details(self, mock_register_record): self.ops.lookup_update({}) self.assertFalse(mock_register_record.called) def test_register_lookup_record(self): self.ops._register_lookup_record( mock.sentinel.provider_addr, mock.sentinel.customer_addr, mock.sentinel.mac_addr, mock.sentinel.vsid) self.ops._nvgre_utils.create_lookup_record.assert_called_once_with( mock.sentinel.provider_addr, mock.sentinel.customer_addr, mock.sentinel.mac_addr, mock.sentinel.vsid) @mock.patch.object(nvgre_ops.HyperVNvgreOps, '_register_lookup_record') def test_bind_nvgre_port(self, mock_register_record): self.ops._nvgre_utils.get_network_iface_ip.return_value = ( mock.sentinel.provider_addr, mock.sentinel.prefix_len) mac_addr = self.ops._hyperv_utils.get_vnic_mac_address.return_value customer_addr = self.ops._n_client.get_port_ip_address.return_value self.ops.bind_nvgre_port(mock.sentinel.vsid, mock.sentinel.network_name, mock.sentinel.port_id) self.ops._hyperv_utils.set_vswitch_port_vsid.assert_called_once_with( mock.sentinel.vsid, mock.sentinel.port_id) mock_register_record.assert_has_calls([ mock.call(mock.sentinel.provider_addr, customer_addr, mac_addr, mock.sentinel.vsid), mock.call(mock.sentinel.ip_addr, constants.IPV4_DEFAULT, mac_addr, mock.sentinel.vsid)]) self.ops._notifier.lookup_update.assert_called_once_with( self.context, mock.sentinel.provider_addr, { 'customer_addr': customer_addr, 'mac_addr': mac_addr, 'customer_vsid': mock.sentinel.vsid }) def test_bind_nvgre_port_no_provider_addr(self): self.ops._nvgre_utils.get_network_iface_ip = mock.MagicMock( return_value=(None, None)) self.ops.bind_nvgre_port(mock.sentinel.vsid, mock.sentinel.network_name, mock.sentinel.port_id) self.assertFalse(self.ops._hyperv_utils.set_vswitch_port_vsid.called) @mock.patch.object(nvgre_ops.HyperVNvgreOps, 'refresh_nvgre_records') @mock.patch.object(nvgre_ops.HyperVNvgreOps, '_create_customer_routes') def test_bind_nvgre_network(self, mock_create_routes, mock_refresh_records): self.config(provider_tunnel_ip=mock.sentinel.ip_addr, group='NVGRE') self.ops._n_client.get_network_subnets.return_value = [ mock.sentinel.subnet, mock.sentinel.subnet2] get_cidr = self.ops._n_client.get_network_subnet_cidr_and_gateway get_cidr.return_value = (self.FAKE_CIDR, mock.sentinel.gateway) self.ops.bind_nvgre_network( mock.sentinel.vsid, mock.sentinel.net_uuid, self.FAKE_VSWITCH_NAME) self.assertEqual(mock.sentinel.vsid, self.ops._network_vsids[mock.sentinel.net_uuid]) self.ops._n_client.get_network_subnets.assert_called_once_with( mock.sentinel.net_uuid) get_cidr.assert_called_once_with(mock.sentinel.subnet) mock_create_routes.assert_called_once_with( mock.sentinel.vsid, self.FAKE_CIDR, mock.sentinel.gateway, mock.ANY) mock_refresh_records.assert_called_once_with( network_id=mock.sentinel.net_uuid) self.ops._notifier.tunnel_update.assert_called_once_with( self.context, mock.sentinel.ip_addr, mock.sentinel.vsid) def _check_create_customer_routes(self, gateway=None): self.ops._create_customer_routes( mock.sentinel.vsid, mock.sentinel.cidr, gateway, mock.sentinel.rdid) self.ops._nvgre_utils.clear_customer_routes.assert_called_once_with( mock.sentinel.vsid) self.ops._nvgre_utils.create_customer_route.assert_called_once_with( mock.sentinel.vsid, mock.sentinel.cidr, constants.IPV4_DEFAULT, mock.sentinel.rdid) def test_create_customer_routes_no_gw(self): self._check_create_customer_routes() def test_create_customer_routes_bad_gw(self): gateway = '10.0.0.1' self._check_create_customer_routes(gateway=gateway) def test_create_customer_routes(self): gateway = '10.0.0.2' self.ops._create_customer_routes( mock.sentinel.vsid, mock.sentinel.cidr, gateway, mock.sentinel.rdid) metadata_addr = '%s/32' % CONF.AGENT.neutron_metadata_address self.ops._nvgre_utils.create_customer_route.assert_has_calls([ mock.call(mock.sentinel.vsid, mock.sentinel.cidr, constants.IPV4_DEFAULT, mock.sentinel.rdid), mock.call(mock.sentinel.vsid, '%s/0' % constants.IPV4_DEFAULT, gateway, mock.ANY), mock.call(mock.sentinel.vsid, metadata_addr, gateway, mock.ANY)], any_order=True) @mock.patch.object(nvgre_ops.HyperVNvgreOps, '_register_lookup_record') def test_refresh_nvgre_records(self, mock_register_record): self.ops._nvgre_ports.append(mock.sentinel.processed_port_id) self.ops._tunneling_agents[mock.sentinel.host_id] = ( mock.sentinel.agent_ip) self.ops._network_vsids[mock.sentinel.net_id] = ( mock.sentinel.vsid) processed_port = {'id': mock.sentinel.processed_port_id} no_host_port = {'id': mock.sentinel.port_no_host_id, 'binding:host_id': mock.sentinel.odd_host_id} other_net_id_port = {'id': mock.sentinel.port_other_net_id, 'binding:host_id': mock.sentinel.host_id, 'network_id': mock.sentinel.odd_net_id} port = {'id': mock.sentinel.port_id, 'binding:host_id': mock.sentinel.host_id, 'network_id': mock.sentinel.net_id, 'mac_address': self.FAKE_MAC_ADDR, 'fixed_ips': [{'ip_address': mock.sentinel.customer_addr}] } self.ops._n_client.get_network_ports.return_value = [ processed_port, no_host_port, other_net_id_port, port] self.ops.refresh_nvgre_records() expected_mac = self.FAKE_MAC_ADDR.replace(':', '') mock_register_record.assert_has_calls([ mock.call(mock.sentinel.agent_ip, mock.sentinel.customer_addr, expected_mac, mock.sentinel.vsid), # mock.call(mock.sentinel.agent_ip, constants.METADATA_ADDR, # expected_mac, mock.sentinel.vsid) ]) self.assertIn(mock.sentinel.port_id, self.ops._nvgre_ports) networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_hyperv_neutron_agent.py0000664000567000056710000005710312677524354031407 0ustar jenkinsjenkins00000000000000# Copyright 2013 Cloudbase Solutions SRL # Copyright 2013 Pedro Navarro Perez # 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. """ Unit tests for Windows Hyper-V virtual switch neutron driver """ from concurrent import futures import time import mock from os_win import exceptions from os_win import utilsfactory from hyperv.neutron import constants from hyperv.neutron import exception from hyperv.neutron import hyperv_neutron_agent from hyperv.tests import base class TestHyperVNeutronAgent(base.BaseTestCase): _FAKE_PORT_ID = 'fake_port_id' def setUp(self): super(TestHyperVNeutronAgent, self).setUp() utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class') utilsfactory_patcher.start() self.addCleanup(utilsfactory_patcher.stop) self.agent = hyperv_neutron_agent.HyperVNeutronAgentMixin() self.agent.plugin_rpc = mock.Mock() self.agent._metricsutils = mock.MagicMock() self.agent._utils = mock.MagicMock() self.agent.sec_groups_agent = mock.MagicMock() self.agent.context = mock.Mock() self.agent.client = mock.MagicMock() self.agent.connection = mock.MagicMock() self.agent.agent_id = mock.Mock() self.agent.notifier = mock.Mock() self.agent._utils = mock.MagicMock() self.agent._nvgre_ops = mock.MagicMock() self.agent._workers = mock.MagicMock() @mock.patch.object(hyperv_neutron_agent, 'synchronized') def test_port_synchronized(self, mock_synchronized): fake_method_side_effect = mock.Mock() @hyperv_neutron_agent._port_synchronized def fake_method(fake_arg, port_id): fake_method_side_effect(fake_arg, port_id) mock_synchronized.return_value = lambda x: x expected_lock_name = 'port-lock-%s' % mock.sentinel.port_id fake_method(fake_arg=mock.sentinel.arg, port_id=mock.sentinel.port_id) mock_synchronized.assert_called_once_with(expected_lock_name) fake_method_side_effect.assert_called_once_with( mock.sentinel.arg, mock.sentinel.port_id) def test_load_physical_network_mappings(self): test_mappings = ['fakenetwork1:fake_vswitch', 'fakenetwork2:fake_vswitch_2', '*:fake_vswitch_3'] expected = [('fakenetwork1', 'fake_vswitch'), ('fakenetwork2', 'fake_vswitch_2'), ('.*', 'fake_vswitch_3')] self.agent._load_physical_network_mappings(test_mappings) self.assertEqual(expected, list(self.agent._physical_network_mappings.items())) @mock.patch.object(hyperv_neutron_agent.nvgre_ops, 'HyperVNvgreOps') def test_init_nvgre_disabled(self, mock_hyperv_nvgre_ops): self.agent._init_nvgre() self.assertFalse(mock_hyperv_nvgre_ops.called) self.assertFalse(self.agent._nvgre_enabled) @mock.patch.object(hyperv_neutron_agent.nvgre_ops, 'HyperVNvgreOps') def test_init_nvgre_no_tunnel_ip(self, mock_hyperv_nvgre_ops): self.config(enable_support=True, group='NVGRE') self.assertRaises(exception.NetworkingHyperVException, self.agent._init_nvgre) @mock.patch.object(hyperv_neutron_agent.nvgre_ops, 'HyperVNvgreOps') def test_init_nvgre_enabled(self, mock_hyperv_nvgre_ops): self.config(enable_support=True, group='NVGRE') self.config(provider_tunnel_ip=mock.sentinel.tunneling_ip, group='NVGRE') self.agent._init_nvgre() mock_hyperv_nvgre_ops.assert_called_once_with( list(self.agent._physical_network_mappings.values())) self.assertTrue(self.agent._nvgre_enabled) self.agent._nvgre_ops.init_notifier.assert_called_once_with( self.agent.context, self.agent.client) def test_get_network_vswitch_map_by_port_id(self): net_uuid = 'net-uuid' self.agent._network_vswitch_map = { net_uuid: {'ports': [self._FAKE_PORT_ID]} } network, port_map = self.agent._get_network_vswitch_map_by_port_id( self._FAKE_PORT_ID) self.assertEqual(net_uuid, network) self.assertEqual({'ports': [self._FAKE_PORT_ID]}, port_map) def test_get_network_vswitch_map_by_port_id_not_found(self): net_uuid = 'net-uuid' self.agent._network_vswitch_map = {net_uuid: {'ports': []}} network, port_map = self.agent._get_network_vswitch_map_by_port_id( self._FAKE_PORT_ID) self.assertIsNone(network) self.assertIsNone(port_map) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_treat_vif_port') def test_port_update_not_found(self, mock_treat_vif_port): self.agent._utils.vnic_port_exists.return_value = False port = {'id': mock.sentinel.port_id} self.agent.port_update(self.agent.context, port) self.assertFalse(mock_treat_vif_port.called) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_treat_vif_port') def test_port_update(self, mock_treat_vif_port): self.agent._utils.vnic_port_exists.return_value = True port = {'id': mock.sentinel.port_id, 'network_id': mock.sentinel.network_id, 'admin_state_up': mock.sentinel.admin_state_up} self.agent.port_update(self.agent.context, port, mock.sentinel.network_type, mock.sentinel.segmentation_id, mock.sentinel.physical_network) mock_treat_vif_port.assert_called_once_with( mock.sentinel.port_id, mock.sentinel.network_id, mock.sentinel.network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id, mock.sentinel.admin_state_up) def test_lookup_update(self): kwargs = {'lookup_ip': mock.sentinel.lookup_ip, 'lookup_details': mock.sentinel.lookup_details} self.agent.lookup_update(mock.sentinel.context, **kwargs) self.agent._nvgre_ops.lookup_update.assert_called_once_with(kwargs) def test_get_vswitch_name_local(self): self.agent._local_network_vswitch = 'test_local_switch' ret = self.agent._get_vswitch_name(constants.TYPE_LOCAL, mock.sentinel.FAKE_PHYSICAL_NETWORK) self.assertEqual('test_local_switch', ret) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, "_get_vswitch_for_physical_network") def test_get_vswitch_name_vlan(self, mock_get_vswitch_for_phys_net): ret = self.agent._get_vswitch_name(constants.TYPE_VLAN, mock.sentinel.FAKE_PHYSICAL_NETWORK) self.assertEqual(mock_get_vswitch_for_phys_net.return_value, ret) mock_get_vswitch_for_phys_net.assert_called_once_with( mock.sentinel.FAKE_PHYSICAL_NETWORK) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, "_get_vswitch_name") def test_provision_network_exception(self, mock_get_vswitch_name): self.assertRaises(exception.NetworkingHyperVException, self.agent._provision_network, mock.sentinel.FAKE_PORT_ID, mock.sentinel.FAKE_NET_UUID, mock.sentinel.FAKE_NETWORK_TYPE, mock.sentinel.FAKE_PHYSICAL_NETWORK, mock.sentinel.FAKE_SEGMENTATION_ID) mock_get_vswitch_name.assert_called_once_with( mock.sentinel.FAKE_NETWORK_TYPE, mock.sentinel.FAKE_PHYSICAL_NETWORK) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, "_get_vswitch_name") def test_provision_network_vlan(self, mock_get_vswitch_name): self.agent._provision_network(mock.sentinel.FAKE_PORT_ID, mock.sentinel.FAKE_NET_UUID, constants.TYPE_VLAN, mock.sentinel.FAKE_PHYSICAL_NETWORK, mock.sentinel.FAKE_SEGMENTATION_ID) mock_get_vswitch_name.assert_called_once_with( constants.TYPE_VLAN, mock.sentinel.FAKE_PHYSICAL_NETWORK) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, "_get_vswitch_name") def test_provision_network_nvgre(self, mock_get_vswitch_name): self.agent._nvgre_enabled = True vswitch_name = mock_get_vswitch_name.return_value self.agent._provision_network(mock.sentinel.FAKE_PORT_ID, mock.sentinel.FAKE_NET_UUID, constants.TYPE_NVGRE, mock.sentinel.FAKE_PHYSICAL_NETWORK, mock.sentinel.FAKE_SEGMENTATION_ID) mock_get_vswitch_name.assert_called_once_with( constants.TYPE_NVGRE, mock.sentinel.FAKE_PHYSICAL_NETWORK) self.agent._nvgre_ops.bind_nvgre_network.assert_called_once_with( mock.sentinel.FAKE_SEGMENTATION_ID, mock.sentinel.FAKE_NET_UUID, vswitch_name) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, "_get_vswitch_name") def test_provision_network_flat(self, mock_get_vswitch_name): self.agent._provision_network(mock.sentinel.FAKE_PORT_ID, mock.sentinel.FAKE_NET_UUID, constants.TYPE_FLAT, mock.sentinel.FAKE_PHYSICAL_NETWORK, mock.sentinel.FAKE_SEGMENTATION_ID) mock_get_vswitch_name.assert_called_once_with( constants.TYPE_FLAT, mock.sentinel.FAKE_PHYSICAL_NETWORK) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, "_get_vswitch_name") def test_provision_network_local(self, mock_get_vswitch_name): self.agent._provision_network(mock.sentinel.FAKE_PORT_ID, mock.sentinel.FAKE_NET_UUID, constants.TYPE_LOCAL, mock.sentinel.FAKE_PHYSICAL_NETWORK, mock.sentinel.FAKE_SEGMENTATION_ID) mock_get_vswitch_name.assert_called_once_with( constants.TYPE_LOCAL, mock.sentinel.FAKE_PHYSICAL_NETWORK) def test_port_bound_enable_metrics(self): self.agent.enable_metrics_collection = True self._test_port_bound(True) def test_port_bound_no_metrics(self): self.agent.enable_metrics_collection = False self._test_port_bound(False) def _test_port_bound(self, enable_metrics): port = mock.MagicMock() net_uuid = 'my-net-uuid' self.agent._port_bound(port, net_uuid, 'vlan', None, None) self.assertEqual(enable_metrics, self.agent._utils.add_metrics_collection_acls.called) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_provision_network') def test_port_bound_nvgre(self, mock_provision_network): self.agent._nvgre_enabled = True network_type = constants.TYPE_NVGRE net_uuid = 'my-net-uuid' fake_map = {'vswitch_name': mock.sentinel.vswitch_name, 'ports': []} def fake_prov_network(*args, **kwargs): self.agent._network_vswitch_map[net_uuid] = fake_map mock_provision_network.side_effect = fake_prov_network self.agent._port_bound(mock.sentinel.port_id, net_uuid, network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id) self.assertIn(mock.sentinel.port_id, fake_map['ports']) mock_provision_network.assert_called_once_with( mock.sentinel.port_id, net_uuid, network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id) self.agent._utils.connect_vnic_to_vswitch.assert_called_once_with( mock.sentinel.vswitch_name, mock.sentinel.port_id) self.agent._nvgre_ops.bind_nvgre_port.assert_called_once_with( mock.sentinel.segmentation_id, mock.sentinel.vswitch_name, mock.sentinel.port_id) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_get_network_vswitch_map_by_port_id') def _check_port_unbound(self, mock_get_vswitch_map_by_port_id, ports=None, net_uuid=None): map = { 'network_type': 'vlan', 'vswitch_name': 'fake-vswitch', 'ports': ports, 'vlan_id': 1} network_vswitch_map = (net_uuid, map) mock_get_vswitch_map_by_port_id.return_value = network_vswitch_map with mock.patch.object( self.agent._utils, 'remove_switch_port') as mock_remove_switch_port: self.agent._port_unbound(self._FAKE_PORT_ID, vnic_deleted=False) if net_uuid: mock_remove_switch_port.assert_called_once_with( self._FAKE_PORT_ID, False) else: self.assertFalse(mock_remove_switch_port.called) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_reclaim_local_network') def test_port_unbound(self, mock_reclaim_local_network): net_uuid = 'my-net-uuid' self._check_port_unbound(ports=[self._FAKE_PORT_ID], net_uuid=net_uuid) mock_reclaim_local_network.assert_called_once_with(net_uuid) def test_port_unbound_port_not_found(self): self._check_port_unbound() def test_port_enable_control_metrics_ok(self): self.agent.enable_metrics_collection = True self.agent._port_metric_retries[self._FAKE_PORT_ID] = ( self.agent._metrics_max_retries) self.agent._utils.is_metrics_collection_allowed.return_value = True self.agent._port_enable_control_metrics() enable_port_metrics_collection = ( self.agent._metricsutils.enable_port_metrics_collection) enable_port_metrics_collection.assert_called_with(self._FAKE_PORT_ID) self.assertNotIn(self._FAKE_PORT_ID, self.agent._port_metric_retries) def test_port_enable_control_metrics_maxed(self): self.agent.enable_metrics_collection = True self.agent._metrics_max_retries = 3 self.agent._port_metric_retries[self._FAKE_PORT_ID] = 3 self.agent._utils.is_metrics_collection_allowed.return_value = False for i in range(4): self.assertIn(self._FAKE_PORT_ID, self.agent._port_metric_retries) self.agent._port_enable_control_metrics() self.assertNotIn(self._FAKE_PORT_ID, self.agent._port_metric_retries) def test_port_enable_control_metrics_no_vnic(self): self.agent.enable_metrics_collection = True self.agent._port_metric_retries[self._FAKE_PORT_ID] = 3 self.agent._utils.is_metrics_collection_allowed.side_effect = ( exceptions.NotFound(resource=self._FAKE_PORT_ID)) self.agent._port_enable_control_metrics() self.assertNotIn(self._FAKE_PORT_ID, self.agent._port_metric_retries) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_port_unbound') def test_vif_port_state_down(self, mock_port_unbound): self.agent._treat_vif_port( mock.sentinel.port_id, mock.sentinel.network_id, mock.sentinel.network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id, False) mock_port_unbound.assert_called_once_with(mock.sentinel.port_id) sg_agent = self.agent.sec_groups_agent sg_agent.remove_devices_filter.assert_called_once_with( [mock.sentinel.port_id]) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_port_bound') def _check_treat_vif_port_state_up(self, mock_port_bound): self.agent._treat_vif_port( mock.sentinel.port_id, mock.sentinel.network_id, mock.sentinel.network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id, True) mock_port_bound.assert_called_once_with( mock.sentinel.port_id, mock.sentinel.network_id, mock.sentinel.network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id) def test_treat_vif_port_sg_enabled(self): self.agent.enable_security_groups = True self._check_treat_vif_port_state_up() sg_agent = self.agent.sec_groups_agent sg_agent.prepare_devices_filter.assert_called_once_with( [mock.sentinel.port_id]) def test_treat_vif_port_sg_disabled(self): self.agent.enable_security_groups = False self._check_treat_vif_port_state_up() self.agent._utils.remove_all_security_rules.assert_called_once_with( mock.sentinel.port_id) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_treat_vif_port') def test_process_added_port(self, mock_treat_vif_port): self.agent._added_ports = set() details = self._get_fake_port_details() self.agent._process_added_port(details) mock_treat_vif_port.assert_called_once_with( mock.sentinel.port_id, mock.sentinel.network_id, mock.sentinel.network_type, mock.sentinel.physical_network, mock.sentinel.segmentation_id, mock.sentinel.admin_state_up) self.agent.plugin_rpc.update_device_up.assert_called_once_with( self.agent.context, mock.sentinel.device, self.agent.agent_id, self.agent._host) self.assertNotIn(mock.sentinel.device, self.agent._added_ports) @mock.patch.object(hyperv_neutron_agent.HyperVNeutronAgentMixin, '_treat_vif_port') def test_process_added_port_failed(self, mock_treat_vif_port): mock_treat_vif_port.side_effect = exception.NetworkingHyperVException self.agent._added_ports = set() details = self._get_fake_port_details() self.agent._process_added_port(details) self.assertIn(mock.sentinel.device, self.agent._added_ports) def _get_fake_port_details(self): return {'device': mock.sentinel.device, 'port_id': mock.sentinel.port_id, 'network_id': mock.sentinel.network_id, 'network_type': mock.sentinel.network_type, 'physical_network': mock.sentinel.physical_network, 'segmentation_id': mock.sentinel.segmentation_id, 'admin_state_up': mock.sentinel.admin_state_up} def test_treat_devices_added_returns_true_for_missing_device(self): self.agent._added_ports = set([mock.sentinel.port_id]) attrs = {'get_devices_details_list.side_effect': Exception()} self.agent.plugin_rpc.configure_mock(**attrs) self.agent._treat_devices_added() self.assertIn(mock.sentinel.port_id, self.agent._added_ports) def test_treat_devices_added_updates_known_port(self): self.agent._added_ports = set([mock.sentinel.device]) details = self._get_fake_port_details() attrs = {'get_devices_details_list.return_value': [details]} self.agent.plugin_rpc.configure_mock(**attrs) self.agent._treat_devices_added() self.agent._workers.submit.assert_called_once_with( self.agent._process_added_port, details) self.assertNotIn(mock.sentinel.device, self.agent._added_ports) def test_treat_devices_added_missing_port_id(self): self.agent._added_ports = set([mock.sentinel.port_id]) details = {'device': mock.sentinel.port_id} attrs = {'get_devices_details_list.return_value': [details]} self.agent.plugin_rpc.configure_mock(**attrs) self.agent._treat_devices_added() self.assertFalse(self.agent._workers.submit.called) self.assertNotIn(mock.sentinel.port_id, self.agent._added_ports) def test_treat_devices_removed_exception(self): self.agent._removed_ports = set([mock.sentinel.port_id]) attrs = {'update_device_down.side_effect': Exception()} self.agent.plugin_rpc.configure_mock(**attrs) self.agent._treat_devices_removed() self.agent.plugin_rpc.update_device_down.assert_called_once_with( self.agent.context, mock.sentinel.port_id, self.agent.agent_id, self.agent._host) self.assertIn(mock.sentinel.port_id, self.agent._removed_ports) def mock_treat_devices_removed(self, port_exists): self.agent._removed_ports = set([mock.sentinel.port_id]) details = dict(exists=port_exists) attrs = {'update_device_down.return_value': details} self.agent.plugin_rpc.configure_mock(**attrs) with mock.patch.object(self.agent, '_port_unbound') as func: self.agent._treat_devices_removed() self.assertEqual(func.called, not port_exists) self.assertEqual( self.agent.sec_groups_agent.remove_devices_filter.called, not port_exists) self.assertNotIn(mock.sentinel.port_id, self.agent._removed_ports) def test_treat_devices_removed_unbinds_port(self): self.mock_treat_devices_removed(False) def test_treat_devices_removed_ignores_missing_port(self): self.mock_treat_devices_removed(False) def test_process_added_port_event(self): self.agent._added_ports = set() self.agent._process_added_port_event(mock.sentinel.port_id) self.assertIn(mock.sentinel.port_id, self.agent._added_ports) def test_process_removed_port_event(self): self.agent._removed_ports = set([]) self.agent._process_removed_port_event(mock.sentinel.port_id) self.assertIn(mock.sentinel.port_id, self.agent._removed_ports) @mock.patch.object(hyperv_neutron_agent.threading, 'Thread') def test_create_event_listeners(self, mock_Thread): self.agent._create_event_listeners() self.agent._utils.get_vnic_event_listener.assert_has_calls([ mock.call(self.agent._utils.EVENT_TYPE_CREATE), mock.call(self.agent._utils.EVENT_TYPE_DELETE)]) target = self.agent._utils.get_vnic_event_listener.return_value calls = [mock.call(target=target, args=(self.agent._process_added_port_event, )), mock.call(target=target, args=(self.agent._process_removed_port_event, ))] mock_Thread.assert_has_calls(calls, any_order=True) self.assertEqual(2, mock_Thread.return_value.start.call_count) def test_thread_pool_execution(self): pool = futures.ThreadPoolExecutor(max_workers=3) mock_fn = mock.MagicMock() for i in range(8): pool.submit(mock_fn, mock.sentinel.parameter) # allow the threads to finish. one second is enough for a noop call. time.sleep(1) mock_fn.assert_has_calls([mock.call(mock.sentinel.parameter)] * 8) networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_mech_hyperv.py0000664000567000056710000000333112677524354027445 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # 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. """ Unit tests for the Hyper-V Mechanism Driver. """ from hyperv.neutron import constants from hyperv.neutron.ml2 import mech_hyperv from hyperv.tests import base class TestHypervMechanismDriver(base.BaseTestCase): def setUp(self): super(TestHypervMechanismDriver, self).setUp() self.mech_hyperv = mech_hyperv.HypervMechanismDriver() def test_get_allowed_network_types(self): agent = {'configurations': {'tunnel_types': []}} actual_net_types = self.mech_hyperv.get_allowed_network_types(agent) network_types = [constants.TYPE_LOCAL, constants.TYPE_FLAT, constants.TYPE_VLAN] self.assertEqual(network_types, actual_net_types) def test_get_allowed_network_types_nvgre(self): agent = {'configurations': {'tunnel_types': [constants.TYPE_NVGRE]}} actual_net_types = self.mech_hyperv.get_allowed_network_types(agent) network_types = [constants.TYPE_LOCAL, constants.TYPE_FLAT, constants.TYPE_VLAN, constants.TYPE_NVGRE] self.assertEqual(network_types, actual_net_types) networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_neutron_client.py0000664000567000056710000001130012677524354030157 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # 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. """ Unit tests for the neutron client. """ import mock from hyperv.neutron import constants from hyperv.neutron import neutron_client from hyperv.tests import base class TestNeutronClient(base.BaseTestCase): _FAKE_CIDR = '10.0.0.0/24' _FAKE_GATEWAY = '10.0.0.1' _FAKE_HOST = 'fake_host' def setUp(self): super(TestNeutronClient, self).setUp() self._neutron = neutron_client.NeutronAPIClient() self._neutron._client = mock.MagicMock() def test_get_network_subnets(self): self._neutron._client.show_network.return_value = { 'network': { 'subnets': [mock.sentinel.fake_subnet] } } subnets = self._neutron.get_network_subnets(mock.sentinel.net_id) self._neutron._client.show_network.assert_called_once_with( mock.sentinel.net_id) self.assertEqual([mock.sentinel.fake_subnet], subnets) def test_get_network_subnets_exception(self): self._neutron._client.show_network.side_effect = Exception("Fail") subnets = self._neutron.get_network_subnets(mock.sentinel.net_id) self.assertEqual([], subnets) def test_get_network_subnet_cidr(self): self._neutron._client.show_subnet.return_value = { 'subnet': { 'cidr': self._FAKE_CIDR, 'gateway_ip': self._FAKE_GATEWAY, } } cidr, gw = self._neutron.get_network_subnet_cidr_and_gateway( mock.sentinel.subnet_id) self._neutron._client.show_subnet.assert_called_once_with( mock.sentinel.subnet_id) self.assertEqual(self._FAKE_CIDR, cidr) self.assertEqual(self._FAKE_GATEWAY, gw) def test_get_network_subnet_cidr_exception(self): self._neutron._client.show_subnet.side_effect = Exception("Fail") cidr, gw = self._neutron.get_network_subnet_cidr_and_gateway( mock.sentinel.subnet_id) self.assertIsNone(cidr) self.assertIsNone(gw) def test_get_port_ip_address(self): self._neutron._client.show_port.return_value = { 'port': { 'fixed_ips': [{'ip_address': mock.sentinel.ip_addr}] } } ip_addr = self._neutron.get_port_ip_address(mock.sentinel.fake_port_id) self._neutron._client.show_port.assert_called_once_with( mock.sentinel.fake_port_id) self.assertEqual(mock.sentinel.ip_addr, ip_addr) def test_get_port_ip_address_exception(self): self._neutron._client.show_port.side_effect = Exception("Fail") ip_addr = self._neutron.get_port_ip_address(mock.sentinel.fake_port_id) self.assertIsNone(ip_addr) def test_get_tunneling_agents(self): non_tunnel_agent = {} ignored_agent = {'configurations': { 'tunnel_types': [constants.TYPE_NVGRE]} } tunneling_agent = { 'configurations': {'tunnel_types': [constants.TYPE_NVGRE], 'tunneling_ip': mock.sentinel.tunneling_ip}, 'host': self._FAKE_HOST } self._neutron._client.list_agents.return_value = { 'agents': [non_tunnel_agent, ignored_agent, tunneling_agent] } actual = self._neutron.get_tunneling_agents() self.assertEqual({self._FAKE_HOST: mock.sentinel.tunneling_ip}, actual) def test_get_tunneling_agents_exception(self): self._neutron._client.list_agents.side_effect = Exception("Fail") actual = self._neutron.get_tunneling_agents() self.assertEqual({}, actual) def test_get_network_ports(self): self._neutron._client.list_ports.return_value = { 'ports': [mock.sentinel.port] } actual = self._neutron.get_network_ports(key='value') self._neutron._client.list_ports.assert_called_once_with(key='value') self.assertEqual([mock.sentinel.port], actual) def test_get_network_ports_exception(self): self._neutron._client.list_ports.side_effect = Exception("Fail") actual = self._neutron.get_network_ports() self.assertEqual([], actual) networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_hyperv_agent_notifier.py0000664000567000056710000000475512677524354031541 0ustar jenkinsjenkins00000000000000# Copyright 2015 Cloudbase Solutions SRL # 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. """ Unit Tests for Hyper-V Agent Notifier. """ import mock from hyperv.neutron import constants from hyperv.neutron import hyperv_agent_notifier from hyperv.tests import base class TestAgentNotifierApi(base.BaseTestCase): def setUp(self): super(TestAgentNotifierApi, self).setUp() self.notifier = hyperv_agent_notifier.AgentNotifierApi( topic=constants.AGENT_TOPIC, client=mock.MagicMock()) def test_tunnel_update(self): expected_topic = hyperv_agent_notifier.get_topic_name( constants.AGENT_TOPIC, constants.TUNNEL, constants.UPDATE) self.notifier.tunnel_update(mock.sentinel.context, mock.sentinel.tunnel_ip, constants.TYPE_NVGRE) self.notifier._client.prepare.assert_called_once_with( topic=expected_topic, fanout=True) prepared_client = self.notifier._client.prepare.return_value prepared_client.cast.assert_called_once_with( mock.sentinel.context, 'tunnel_update', tunnel_ip=mock.sentinel.tunnel_ip, tunnel_type=constants.TYPE_NVGRE) def test_lookup_update(self): expected_topic = hyperv_agent_notifier.get_topic_name( constants.AGENT_TOPIC, constants.LOOKUP, constants.UPDATE) self.notifier.lookup_update(mock.sentinel.context, mock.sentinel.lookup_ip, mock.sentinel.lookup_details) self.notifier._client.prepare.assert_called_once_with( topic=expected_topic, fanout=True) prepared_client = self.notifier._client.prepare.return_value prepared_client.cast.assert_called_once_with( mock.sentinel.context, 'lookup_update', lookup_ip=mock.sentinel.lookup_ip, lookup_details=mock.sentinel.lookup_details) networking-hyperv-2.0.0/hyperv/tests/unit/neutron/test_security_groups_driver.py0000664000567000056710000005266312677524354031771 0ustar jenkinsjenkins00000000000000# Copyright 2014 Cloudbase Solutions SRL # 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. """ Unit tests for the Hyper-V Security Groups Driver. """ import mock from os_win import exceptions from os_win import utilsfactory from oslo_config import cfg from hyperv.neutron import security_groups_driver as sg_driver from hyperv.tests import base CONF = cfg.CONF class SecurityGroupRuleTestHelper(base.BaseTestCase): _FAKE_DIRECTION = 'egress' _FAKE_ETHERTYPE = 'IPv4' _FAKE_ETHERTYPE_IPV6 = 'IPv6' _FAKE_PROTOCOL = 'tcp' _FAKE_ACTION = sg_driver.ACL_PROP_MAP['action']['allow'] _FAKE_DEST_IP_PREFIX = '10.0.0.0/24' _FAKE_SOURCE_IP_PREFIX = '10.0.1.0/24' _FAKE_MEMBER_IP = '10.0.0.1' _FAKE_IPV6_LEN128_IP = 'fddd:cafd:e664:0:f816:3eff:fe8d:59d2/128' _FAKE_SG_ID = 'fake_sg_id' _FAKE_PORT_MIN = 9001 _FAKE_PORT_MAX = 9011 def _create_security_rule(self): return { 'direction': self._FAKE_DIRECTION, 'ethertype': self._FAKE_ETHERTYPE, 'protocol': self._FAKE_PROTOCOL, 'dest_ip_prefix': self._FAKE_DEST_IP_PREFIX, 'source_ip_prefix': self._FAKE_SOURCE_IP_PREFIX, 'port_range_min': self._FAKE_PORT_MIN, 'port_range_max': self._FAKE_PORT_MAX, 'security_group_id': self._FAKE_SG_ID } @classmethod def _acl(self, key1, key2): return sg_driver.ACL_PROP_MAP[key1][key2] class TestHyperVSecurityGroupsDriver(SecurityGroupRuleTestHelper): _FAKE_DEVICE = 'fake_device' _FAKE_ID = 'fake_id' _FAKE_PARAM_NAME = 'fake_param_name' _FAKE_PARAM_VALUE = 'fake_param_value' def setUp(self): super(TestHyperVSecurityGroupsDriver, self).setUp() utilsfactory_patcher = mock.patch.object(utilsfactory, '_get_class') utilsfactory_patcher.start() self.addCleanup(utilsfactory_patcher.stop) self._driver = sg_driver.HyperVSecurityGroupsDriver() self._driver._utils = mock.MagicMock() self._driver._sg_gen = mock.MagicMock() def test__select_sg_rules_for_port(self): mock_port = self._get_port() mock_port['fixed_ips'] = [mock.MagicMock()] mock_port['security_groups'] = [self._FAKE_SG_ID] fake_sg_template = self._create_security_rule() fake_sg_template['direction'] = 'ingress' self._driver._sg_rule_templates[self._FAKE_SG_ID] = [fake_sg_template] # Test without remote_group_id rule_list = self._driver._select_sg_rules_for_port(mock_port, 'ingress') self.assertEqual(self._FAKE_SG_ID, rule_list[0]['security_group_id']) # Test with remote_group_id fake_sg_template['remote_group_id'] = self._FAKE_SG_ID self._driver._sg_members[self._FAKE_SG_ID] = {self._FAKE_ETHERTYPE: [self._FAKE_MEMBER_IP]} rule_list = self._driver._select_sg_rules_for_port(mock_port, 'ingress') self.assertEqual(self._FAKE_SG_ID, rule_list[0]['security_group_id']) self.assertEqual('10.0.0.1/32', rule_list[0]['source_ip_prefix']) # Test for fixed 'ip' existing in 'sg_members' self._driver._sg_members[self._FAKE_SG_ID][self._FAKE_ETHERTYPE] = [ '10.0.0.2'] mock_port['fixed_ips'] = ['10.0.0.2'] rule_list = self._driver._select_sg_rules_for_port(mock_port, 'ingress') self.assertEqual([], rule_list) # Test for 'egress' direction fake_sg_template['direction'] = 'egress' fix_ip = [self._FAKE_MEMBER_IP, '10.0.0.2'] self._driver._sg_members[self._FAKE_SG_ID][self._FAKE_ETHERTYPE] = ( fix_ip) rule_list = self._driver._select_sg_rules_for_port(mock_port, 'egress') self.assertEqual('10.0.0.1/32', rule_list[0]['dest_ip_prefix']) # Test for rules with a different direction rule_list = self._driver._select_sg_rules_for_port(mock_port, 'ingress') self.assertEqual([], rule_list) def test_update_security_group_rules(self): mock_rule = [self._create_security_rule()] self._driver.update_security_group_rules(self._FAKE_ID, mock_rule) self.assertEqual(mock_rule, self._driver._sg_rule_templates[self._FAKE_ID]) def test_update_security_group_members(self): mock_member = ['10.0.0.1/32'] self._driver.update_security_group_members(self._FAKE_ID, mock_member) self.assertEqual(mock_member, self._driver._sg_members[self._FAKE_ID]) @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_select_sg_rules_for_port') def test__generate_rules(self, mock_select_sg_rules): mock_rule = [self._create_security_rule()] mock_port = self._get_port() mock_select_sg_rules.return_value = mock_rule ports = self._driver._generate_rules([mock_port]) # Expected result mock_rule.append(mock_rule[0]) expected = {self._FAKE_ID: mock_rule} self.assertEqual(expected, ports) @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_generate_rules') @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_create_port_rules') @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_add_sg_port_rules') def test_prepare_port_filter(self, mock_add_rules, mock_create_rules, mock_gen_rules): mock_port = self._get_port() mock_create_default = self._driver._sg_gen.create_default_sg_rules fake_rule = self._create_security_rule() self._driver._get_rule_remote_address = mock.MagicMock( return_value=self._FAKE_SOURCE_IP_PREFIX) mock_gen_rules.return_value = {mock_port['id']: [fake_rule]} self._driver.prepare_port_filter(mock_port) self.assertEqual(mock_port, self._driver._security_ports[self._FAKE_DEVICE]) mock_gen_rules.assert_called_with([self._driver._security_ports [self._FAKE_DEVICE]]) mock_add_rules.assert_called_once_with( self._FAKE_ID, mock_create_default.return_value) self._driver._create_port_rules.assert_called_with( self._FAKE_ID, [fake_rule]) @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_generate_rules') def test_update_port_filter(self, mock_gen_rules): mock_port = self._get_port() new_mock_port = self._get_port() new_mock_port['id'] += '2' new_mock_port['security_group_rules'][0]['ethertype'] += "2" fake_rule_new = self._create_security_rule() self._driver._get_rule_remote_address = mock.MagicMock( return_value=self._FAKE_SOURCE_IP_PREFIX) mock_gen_rules.return_value = {new_mock_port['id']: [fake_rule_new]} self._driver._security_ports[mock_port['device']] = mock_port self._driver._sec_group_rules[new_mock_port['id']] = [] self._driver._create_port_rules = mock.MagicMock() self._driver._remove_port_rules = mock.MagicMock() self._driver.update_port_filter(new_mock_port) self._driver._remove_port_rules.assert_called_once_with( mock_port['id'], mock_port['security_group_rules']) self._driver._create_port_rules.assert_called_once_with( new_mock_port['id'], [new_mock_port['security_group_rules'][0], fake_rule_new]) self.assertEqual(new_mock_port, self._driver._security_ports[new_mock_port['device']]) @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, 'prepare_port_filter') def test_update_port_filter_new_port(self, mock_method): mock_port = self._get_port() new_mock_port = self._get_port() new_mock_port['id'] += '2' new_mock_port['device'] += '2' new_mock_port['security_group_rules'][0]['ethertype'] += "2" self._driver._security_ports[mock_port['device']] = mock_port self._driver.update_port_filter(new_mock_port) self.assertNotIn(new_mock_port['device'], self._driver._security_ports) def test_remove_port_filter(self): mock_port = self._get_port() mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [mock_rule] self._driver._security_ports[mock_port['device']] = mock_port self._driver.remove_port_filter(mock_port) self.assertNotIn(mock_port['device'], self._driver._security_ports) self.assertNotIn(mock_port['id'], self._driver._sec_group_rules) self._driver._utils.clear_port_sg_acls_cache(mock_port['id']) @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_add_sg_port_rules') @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_remove_sg_port_rules') def test_create_port_rules(self, mock_remove, mock_add): mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [mock_rule] self._driver._sg_gen.create_security_group_rules.return_value = [ mock_rule] self._driver._sg_gen.compute_new_rules_add.return_value = ( [mock_rule, mock_rule], [mock_rule, mock_rule]) self._driver._create_port_rules(self._FAKE_ID, [mock_rule]) self._driver._sg_gen.compute_new_rules_add.assert_called_once_with( [mock_rule], [mock_rule]) mock_remove.assert_called_once_with(self._FAKE_ID, [mock_rule]) mock_add.assert_called_once_with(self._FAKE_ID, [mock_rule]) @mock.patch.object(sg_driver.HyperVSecurityGroupsDriver, '_remove_sg_port_rules') def test_remove_port_rules(self, mock_remove): mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [mock_rule] self._driver._sg_gen.create_security_group_rules.return_value = [ mock_rule] self._driver._remove_port_rules(self._FAKE_ID, [mock_rule]) mock_remove.assert_called_once_with(self._FAKE_ID, [mock_rule]) def test_add_sg_port_rules_exception(self): mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [] self._driver._utils.create_security_rules.side_effect = ( exceptions.HyperVException(msg='Generated Exception for testing.')) self.assertRaises(exceptions.HyperVException, self._driver._add_sg_port_rules, self._FAKE_ID, [mock_rule]) self.assertNotIn(mock_rule, self._driver._sec_group_rules[self._FAKE_ID]) def test_add_sg_port_rules_port_not_found(self): self._driver._sec_group_rules[self._FAKE_ID] = [] self._driver._utils.create_security_rules.side_effect = ( exceptions.NotFound(resource='port_id')) self.assertRaises(exceptions.NotFound, self._driver._add_sg_port_rules, self._FAKE_ID, [mock.sentinel.rule]) self.assertNotIn(self._FAKE_ID, self._driver._sec_group_rules) def test_add_sg_port_rules(self): mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [] self._driver._add_sg_port_rules(self._FAKE_ID, [mock_rule]) self._driver._utils.create_security_rules.assert_called_once_with( self._FAKE_ID, [mock_rule]) self.assertIn(mock_rule, self._driver._sec_group_rules[self._FAKE_ID]) def test_add_sg_port_rules_empty(self): self._driver._add_sg_port_rules(mock.sentinel.id, []) self.assertFalse(self._driver._utils.create_security_rules.called) def test_remove_sg_port_rules_exception(self): mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [mock_rule] self._driver._utils.remove_security_rules.side_effect = ( exceptions.HyperVException(msg='Generated Exception for testing.')) self.assertRaises(exceptions.HyperVException, self._driver._remove_sg_port_rules, self._FAKE_ID, [mock_rule]) self.assertIn(mock_rule, self._driver._sec_group_rules[self._FAKE_ID]) def test_remove_sg_port_rules_port_not_found(self): self._driver._sec_group_rules[self._FAKE_ID] = [] self._driver._utils.remove_security_rules.side_effect = ( exceptions.NotFound(resource='port_id')) self.assertRaises(exceptions.NotFound, self._driver._remove_sg_port_rules, self._FAKE_ID, [mock.sentinel.rule]) self.assertNotIn(self._FAKE_ID, self._driver._sec_group_rules) def test_remove_sg_port_rules(self): mock_rule = mock.MagicMock() self._driver._sec_group_rules[self._FAKE_ID] = [mock_rule] self._driver._remove_sg_port_rules( self._FAKE_ID, [mock_rule, mock.sentinel.other_rule]) self._driver._utils.remove_security_rules.assert_called_once_with( self._FAKE_ID, [mock_rule, mock.sentinel.other_rule]) self.assertNotIn(mock_rule, self._driver._sec_group_rules[self._FAKE_ID]) def test_remove_sg_port_rules_empty(self): self._driver._remove_sg_port_rules(mock.sentinel.id, []) self.assertFalse(self._driver._utils.remove_security_rules.called) def _get_port(self): return { 'device': self._FAKE_DEVICE, 'id': self._FAKE_ID, 'security_group_rules': [mock.MagicMock()] } class SecurityGroupRuleR2BaseTestCase(SecurityGroupRuleTestHelper): def _create_sg_rule(self, protocol=None, action=None, direction='egress'): protocol = protocol or self._FAKE_PROTOCOL action = action or self._FAKE_ACTION remote_addr = (self._FAKE_DEST_IP_PREFIX if direction is 'egress' else self._FAKE_SOURCE_IP_PREFIX) return sg_driver.SecurityGroupRuleR2( self._acl('direction', self._FAKE_DIRECTION), '%s-%s' % (self._FAKE_PORT_MIN, self._FAKE_PORT_MAX), protocol, remote_addr, action) class SecurityGroupRuleGeneratorTestCase(SecurityGroupRuleR2BaseTestCase): def setUp(self): super(SecurityGroupRuleGeneratorTestCase, self).setUp() self.sg_gen = sg_driver.SecurityGroupRuleGenerator() @mock.patch.object(sg_driver.SecurityGroupRuleGenerator, 'create_security_group_rule') def test_create_security_group_rules(self, mock_create_sec_group_rule): sg_rule = self._create_sg_rule() mock_create_sec_group_rule.return_value = [sg_rule] expected = [sg_rule] * 2 rules = [self._create_security_rule()] * 2 actual = self.sg_gen.create_security_group_rules(rules) self.assertEqual(expected, actual) def test_convert_any_address_to_same_ingress(self): rule = self._create_security_rule() rule['direction'] = 'ingress' actual = self.sg_gen._get_rule_remote_address(rule) self.assertEqual(self._FAKE_SOURCE_IP_PREFIX, actual) def test_convert_any_address_to_same_egress(self): rule = self._create_security_rule() rule['direction'] += '2' actual = self.sg_gen._get_rule_remote_address(rule) self.assertEqual(self._FAKE_DEST_IP_PREFIX, actual) def test_convert_any_address_to_ipv4(self): rule = self._create_security_rule() del rule['dest_ip_prefix'] actual = self.sg_gen._get_rule_remote_address(rule) self.assertEqual(self._acl('address_default', 'IPv4'), actual) def test_convert_any_address_to_ipv6(self): rule = self._create_security_rule() del rule['dest_ip_prefix'] rule['ethertype'] = self._FAKE_ETHERTYPE_IPV6 actual = self.sg_gen._get_rule_remote_address(rule) self.assertEqual(self._acl('address_default', 'IPv6'), actual) class SecurityGroupRuleGeneratorR2TestCase(SecurityGroupRuleR2BaseTestCase): def setUp(self): super(SecurityGroupRuleGeneratorR2TestCase, self).setUp() self.sg_gen = sg_driver.SecurityGroupRuleGeneratorR2() def test_create_security_group_rule(self): expected = [self._create_sg_rule()] rule = self._create_security_rule() actual = self.sg_gen.create_security_group_rule(rule) self.assertEqual(expected, actual) def test_create_security_group_rule_len128(self): expected = [self._create_sg_rule()] expected[0].RemoteIPAddress = self._FAKE_IPV6_LEN128_IP.split( '/128', 1)[0] rule = self._create_security_rule() rule['dest_ip_prefix'] = self._FAKE_IPV6_LEN128_IP actual = self.sg_gen.create_security_group_rule(rule) self.assertEqual(expected, actual) def test_create_security_group_rule_any(self): sg_rule1 = self._create_sg_rule(self._acl('protocol', 'tcp')) sg_rule2 = self._create_sg_rule(self._acl('protocol', 'udp')) sg_rule3 = self._create_sg_rule(self._acl('protocol', 'icmp')) sg_rule4 = self._create_sg_rule(self._acl('protocol', 'ipv6-icmp')) rule = self._create_security_rule() rule['protocol'] = sg_driver.ACL_PROP_MAP["default"] actual = self.sg_gen.create_security_group_rule(rule) expected = [sg_rule1, sg_rule2, sg_rule3, sg_rule4] self.assertEqual(sorted(expected), sorted(actual)) def test_create_default_sg_rules(self): actual = self.sg_gen.create_default_sg_rules() self.assertEqual(16, len(actual)) def test_compute_new_rules_add(self): new_rule = self._create_sg_rule() old_rule = self._create_sg_rule() old_rule.Direction = mock.sentinel.FAKE_DIRECTION add_rules, remove_rules = self.sg_gen.compute_new_rules_add( [old_rule], [new_rule, old_rule]) self.assertEqual([new_rule], add_rules) def test_get_rule_port_range(self): rule = self._create_security_rule() expected = '%s-%s' % (self._FAKE_PORT_MIN, self._FAKE_PORT_MAX) actual = self.sg_gen._get_rule_port_range(rule) self.assertEqual(expected, actual) def test_get_rule_port_range_default(self): rule = self._create_security_rule() del rule['port_range_min'] expected = sg_driver.ACL_PROP_MAP['default'] actual = self.sg_gen._get_rule_port_range(rule) self.assertEqual(expected, actual) def test_get_rule_protocol_icmp(self): self._check_get_rule_protocol('icmp', self._acl('protocol', 'icmp')) def test_get_rule_protocol_no_icmp(self): self._check_get_rule_protocol('tcp', 'tcp') def _check_get_rule_protocol(self, protocol, expected): rule = self._create_security_rule() rule['protocol'] = protocol actual = self.sg_gen._get_rule_protocol(rule) self.assertEqual(expected, actual) class SecurityGroupRuleR2TestCase(SecurityGroupRuleR2BaseTestCase): def test_sg_rule_to_dict(self): expected = {'Direction': self._acl('direction', self._FAKE_DIRECTION), 'Action': self._FAKE_ACTION, 'Protocol': self._FAKE_PROTOCOL, 'LocalPort': '%s-%s' % (self._FAKE_PORT_MIN, self._FAKE_PORT_MAX), 'RemoteIPAddress': self._FAKE_DEST_IP_PREFIX, 'Stateful': True, 'IdleSessionTimeout': 0} sg_rule = self._create_sg_rule() self.assertEqual(expected, sg_rule.to_dict()) def test_localport(self): sg_rule = self._create_sg_rule() expected = '%s-%s' % (self._FAKE_PORT_MIN, self._FAKE_PORT_MAX) self.assertEqual(expected, sg_rule.LocalPort) def test_localport_icmp(self): sg_rule = self._create_sg_rule(self._acl('protocol', 'icmp')) self.assertEqual('', sg_rule.LocalPort) def test_stateful_icmp(self): sg_rule = self._create_sg_rule(self._acl('protocol', 'icmp')) self.assertFalse(sg_rule.Stateful) def test_stateful_ipv6_icmp(self): sg_rule = self._create_sg_rule(self._acl('protocol', 'ipv6-icmp')) self.assertFalse(sg_rule.Stateful) def test_stateful_deny(self): sg_rule = self._create_sg_rule(action=self._acl('action', 'deny')) self.assertFalse(sg_rule.Stateful) def test_stateful_true(self): sg_rule = self._create_sg_rule() self.assertTrue(sg_rule.Stateful) def test_rule_uniqueness(self): sg_rule = self._create_sg_rule() sg_rule2 = self._create_sg_rule(self._acl('protocol', 'icmp')) self.assertEqual([sg_rule], list(set([sg_rule] * 2))) self.assertEqual(sorted([sg_rule, sg_rule2]), sorted(list(set([sg_rule, sg_rule2])))) networking-hyperv-2.0.0/hyperv/tests/unit/__init__.py0000664000567000056710000000000012677524354024130 0ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/PKG-INFO0000664000567000056710000000677012677524665017507 0ustar jenkinsjenkins00000000000000Metadata-Version: 1.1 Name: networking-hyperv Version: 2.0.0 Summary: This project tracks the work to integrate the Hyper-V networking with Neutron. This project contains the Hyper-V Neutron Agent Mixin, Security Groups Driver, ML2 Mechanism Driver and the utils modules they use in order to properly bind neutron ports on a Hyper-V host. This project resulted from the neutron core vendor decomposition. Home-page: http://www.cloudbase.it/ Author: Cloudbase Solutions Srl Author-email: info@cloudbasesolutions.com License: Apache License, Version 2.0 Description: ================= networking-hyperv ================= This project tracks the work to integrate the Hyper-V networking with Neutron. This project contains the Hyper-V Neutron Agent Mixin, Security Groups Driver, ML2 Mechanism Driver and the utils modules they use in order to properly bind neutron ports on a Hyper-V host. This project resulted from the neutron core vendor decomposition. Supports Python 2.7 and Python 3.3. * Free software: Apache license * Documentation: http://docs.openstack.org/developer/networking-hyperv * Source: http://git.openstack.org/cgit/openstack/networking-hyperv * Bugs: http://bugs.launchpad.net/networking-hyperv How to Install -------------- Run the following command to install the agent in the system: :: C:\networking-hyperv> python setup.py install To properly use the agent, you will have to set the core_plugin in ``neutron.conf`` to: :: core_plugin = neutron.plugins.ml2.plugin.Ml2Plugin Additionally, you will have to add Hyper-V as a mechanism in ``ml2_conf.ini``: :: mechanism_drivers = openvswitch,hyperv Finally, make sure the tenant_network_types field contains network types supported by Hyper-V: local, flat, vlan. Tests ----- You will have to install the test dependencies first to be able to run the tests. :: C:\networking-hyperv> pip install -r test-requirements.txt You can run the unit tests with the following command. :: C:\networking-hyperv> nosetests hyperv\tests HACKING ------- To contribute to this repo, please go through the following steps. 1. Keep your working tree updated 2. Make modifications on your working tree 3. Run tests 4. If the tests pass, create a pull request on our github repo. 5. Wait for the pull request to be reviewed. Features -------- * TODO Keywords: openstack neutron hyper-v networking 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 :: Microsoft :: Windows Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 networking-hyperv-2.0.0/tools/0000775000567000056710000000000012677524665017540 5ustar jenkinsjenkins00000000000000networking-hyperv-2.0.0/tools/tox_install.sh0000775000567000056710000000234112677524354022432 0ustar jenkinsjenkins00000000000000#!/bin/sh # Many of neutron's repos suffer from the problem of depending on neutron, # but it not existing on pypi. # This wrapper for tox's package installer will use the existing package # if it exists, else use zuul-cloner if that program exists, else grab it # from neutron master via a hard-coded URL. That last case should only # happen with devs running unit tests locally. # From the tox.ini config page: # install_command=ARGV # default: # pip install {opts} {packages} ZUUL_CLONER=/usr/zuul-env/bin/zuul-cloner neutron_installed=$(echo "import neutron" | python 2>/dev/null ; echo $?) set -e if [ $neutron_installed -eq 0 ]; then echo "ALREADY INSTALLED" > /tmp/tox_install.txt echo "Neutron already installed; using existing package" elif [ -x "$ZUUL_CLONER" ]; then export ZUUL_BRANCH=${ZUUL_BRANCH-$BRANCH} echo "ZUUL CLONER" > /tmp/tox_install.txt cwd=$(/bin/pwd) cd /tmp $ZUUL_CLONER --cache-dir \ /opt/git \ git://git.openstack.org \ openstack/neutron cd openstack/neutron pip install -e . cd "$cwd" else echo "PIP HARDCODE" > /tmp/tox_install.txt pip install -U -egit+https://git.openstack.org/openstack/neutron#egg=neutron fi pip install -U $* exit $? networking-hyperv-2.0.0/setup.cfg0000664000567000056710000000346212677524665020226 0ustar jenkinsjenkins00000000000000[metadata] name = networking-hyperv summary = This project tracks the work to integrate the Hyper-V networking with Neutron. This project contains the Hyper-V Neutron Agent Mixin, Security Groups Driver, ML2 Mechanism Driver and the utils modules they use in order to properly bind neutron ports on a Hyper-V host. This project resulted from the neutron core vendor decomposition. description-file = README.rst license = Apache License, Version 2.0 author = Cloudbase Solutions Srl author-email = info@cloudbasesolutions.com home-page = http://www.cloudbase.it/ url = https://github.com/cloudbase/neutron-hyperv-agent classifier = Environment :: OpenStack Intended Audience :: Information Technology Intended Audience :: System Administrators License :: OSI Approved :: Apache Software License Operating System :: Microsoft :: Windows Programming Language :: Python Programming Language :: Python :: 2 Programming Language :: Python :: 2.7 Programming Language :: Python :: 3 Programming Language :: Python :: 3.3 Programming Language :: Python :: 3.4 keywords = openstack neutron hyper-v networking [files] packages = hyperv [entry_points] console_scripts = neutron-hyperv-agent = hyperv.neutron.l2_agent:main neutron.ml2.mechanism_drivers = hyperv = hyperv.neutron.ml2.mech_hyperv:HypervMechanismDriver [build_sphinx] source-dir = doc/source build-dir = doc/build all_files = 1 [upload_sphinx] upload-dir = doc/build/html [compile_catalog] directory = hyperv/locale domain = networking-hyperv [update_catalog] domain = networking-hyperv output_dir = hyperv/locale input_file = hyperv/locale/networking-hyperv.pot [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext mapping_file = babel.cfg output_file = hyperv/locale/networking-hyperv.pot [egg_info] tag_build = tag_svn_revision = 0 tag_date = 0 networking-hyperv-2.0.0/HACKING.rst0000664000567000056710000000025112677524354020167 0ustar jenkinsjenkins00000000000000networking-hyperv Style Commandments =============================================== Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/ networking-hyperv-2.0.0/.mailmap0000664000567000056710000000013112677524354020007 0ustar jenkinsjenkins00000000000000# Format is: # # networking-hyperv-2.0.0/.testr.conf0000664000567000056710000000047712677524354020471 0ustar jenkinsjenkins00000000000000[DEFAULT] test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \ OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \ OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \ ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list networking-hyperv-2.0.0/test-requirements.txt0000664000567000056710000000105712677524354022637 0ustar jenkinsjenkins00000000000000# The order of packages is significant, because pip processes them in the order # of appearance. Changing the order has an impact on the overall integration # process, which may cause wedges in the gate later. hacking<0.11,>=0.10.0 coverage>=3.6 # Apache-2.0 fixtures>=1.3.1 # Apache-2.0/BSD mock>=1.2 # BSD python-subunit>=0.0.18 # Apache-2.0/BSD sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2 # BSD oslosphinx!=3.4.0,>=2.5.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0 testrepository>=0.0.18 # Apache-2.0/BSD testscenarios>=0.4 # Apache-2.0/BSD testtools>=1.4.0 # MIT networking-hyperv-2.0.0/README.rst0000664000567000056710000000336712677524354020073 0ustar jenkinsjenkins00000000000000================= networking-hyperv ================= This project tracks the work to integrate the Hyper-V networking with Neutron. This project contains the Hyper-V Neutron Agent Mixin, Security Groups Driver, ML2 Mechanism Driver and the utils modules they use in order to properly bind neutron ports on a Hyper-V host. This project resulted from the neutron core vendor decomposition. Supports Python 2.7 and Python 3.3. * Free software: Apache license * Documentation: http://docs.openstack.org/developer/networking-hyperv * Source: http://git.openstack.org/cgit/openstack/networking-hyperv * Bugs: http://bugs.launchpad.net/networking-hyperv How to Install -------------- Run the following command to install the agent in the system: :: C:\networking-hyperv> python setup.py install To properly use the agent, you will have to set the core_plugin in ``neutron.conf`` to: :: core_plugin = neutron.plugins.ml2.plugin.Ml2Plugin Additionally, you will have to add Hyper-V as a mechanism in ``ml2_conf.ini``: :: mechanism_drivers = openvswitch,hyperv Finally, make sure the tenant_network_types field contains network types supported by Hyper-V: local, flat, vlan. Tests ----- You will have to install the test dependencies first to be able to run the tests. :: C:\networking-hyperv> pip install -r test-requirements.txt You can run the unit tests with the following command. :: C:\networking-hyperv> nosetests hyperv\tests HACKING ------- To contribute to this repo, please go through the following steps. 1. Keep your working tree updated 2. Make modifications on your working tree 3. Run tests 4. If the tests pass, create a pull request on our github repo. 5. Wait for the pull request to be reviewed. Features -------- * TODO networking-hyperv-2.0.0/tox.ini0000664000567000056710000000153112677524354017706 0ustar jenkinsjenkins00000000000000[tox] minversion = 1.6 envlist = py34,py27,pypy,pep8 skipsdist = True [testenv] usedevelop = True install_command = {toxinidir}/tools/tox_install.sh {opts} {packages} setenv = VIRTUAL_ENV={envdir} deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt commands = python setup.py testr --slowest --testr-args='{posargs}' [testenv:pep8] commands = flake8 [testenv:venv] commands = {posargs} [testenv:cover] commands = python setup.py testr --coverage --testr-args='{posargs}' [testenv:docs] commands = python setup.py build_sphinx [testenv:debug] commands = oslo_debug_helper {posargs} [flake8] # H803 skipped on purpose per list discussion. # E123, E125 skipped as they are invalid PEP-8. show-source = True ignore = E123,E125,H803 builtins = _ exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build networking-hyperv-2.0.0/setup.py0000664000567000056710000000200412677524354020101 0ustar jenkinsjenkins00000000000000# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or # implied. # See the License for the specific language governing permissions and # limitations under the License. # THIS FILE IS MANAGED BY THE GLOBAL REQUIREMENTS REPO - DO NOT EDIT import setuptools # In python < 2.7.4, a lazy loading of package `pbr` will break # setuptools if some other modules registered functions in `atexit`. # solution from: http://bugs.python.org/issue15881#msg170215 try: import multiprocessing # noqa except ImportError: pass setuptools.setup( setup_requires=['pbr>=1.8'], pbr=True) networking-hyperv-2.0.0/AUTHORS0000664000567000056710000000120112677524664017441 0ustar jenkinsjenkins00000000000000Adelina Tuvenie Alessandro Pilotti Claudiu B Claudiu Belu Doug Hellmann Gergo Debreczeni Lucian Petrut Monty Taylor Tony Breeds Vinod Kumar janonymous krishna kanth krishna mallela sonu sonu sudhakaran networking-hyperv-2.0.0/babel.cfg0000664000567000056710000000002112677524354020112 0ustar jenkinsjenkins00000000000000[python: **.py] networking-hyperv-2.0.0/MANIFEST.in0000664000567000056710000000013612677524354020131 0ustar jenkinsjenkins00000000000000include AUTHORS include ChangeLog exclude .gitignore exclude .gitreview global-exclude *.pyc networking-hyperv-2.0.0/ChangeLog0000664000567000056710000000760312677524664020157 0ustar jenkinsjenkins00000000000000CHANGES ======= 2.0.0 ----- * Synchronize port handling * SecurityGroups: clears port SG rule cache if port doesn't exist * Adds try-catch for NotFound exception when enabling metrics * Checks if rule exists before removing from list * Fixes sg_acls_sds map pop issue * Switches to post-versioning * Updated from global requirements * Removes WMI and pywin32 requirement * Adds LOG.exception when an exception is raised * Replaces in-branch utils with os_win utils * Adds trace to the port processing logic * Updated from global requirements * Scaling hyperv neutron agent * Updated from global requirements * Enhanced RPC support in Hyper-V * Adds native thread pool * Replaces vNIC periodic query with event listeners * Ensure security groups are properly rebound * Updates HyperVSecurityGroupsDriver icmpv6 protocol * Updated from global requirements * Refactors disconnect_switch_port * Removes deleted port from Hyper-V Security Groups Driver cache * Create a new object instead of querying the default one * py26/py33 are no longer supported by Infra's CI * remove python 2.6 trove classifier * Manual global-requirements sync * Reraise exception raised by security groups driver * Adds HyperVSecurityGroupsDriver parent class * Moves get_agent_configurations to l2_agent * Adds Neutron Agent initialisation and executable * Updates requirements.txt and test-requirements.txt * Caches Hyper-V vSwitches * Adds security_group_updated method in HyperVSecurityGroupDriver * Fixes bug: AttributeError when trying to use local_network_vswitch * Fixes IPv6 Security Group rules with 128 prefix length * Updates project repo to openstack * Avoid wmi query while creating an acl * Caches Security Group Rule ACLs * Caches switch port, VLAN, VSID setting data WMI objects * Avoids rebinding the same security group rules * Updates requirements.txt and test-requirements.txt * Caches VirtualSystemManagementService and MetricService * Assures that security group rules are added first * Removes useless field initialisation * Small cleanup after py3 compatibility integration * Adds Python 3 compatibility * Change ignore-errors to ignore_errors * Adds Hyper-V NVGRE support * Adds Hyper-V Neutron Agent NVGRE tunnel type spec * Hyper-V: Removes extra cases for ingress ICMP rule * Replaces oslo.i18n with oslo_i18n * Hyper-V: Makes sure empty array rules are not processed * Hyper-V: Fixes Security Group Driver ICMPv6 rules * Update requirements.txt and test-requirements.txt * Bump version to 2015.2.0 2015.1.0 -------- * Bump version to 2015.1.0 * Hyper-V: Adds multiple security group rules * Refactors Hyper-V Security Groups * Adds SecurityGroupRule and Generator * Bump version to 1.0.4 * Hyper-V: Fixes unsupported 'TRUNK' endpoint mode * Hyperv agent does not respect remoteIpPrefix 1.0.3 ----- * Fixes the port_delete call of inexistent port issue * Fixes Hyper-V Agent state reporting under scale scenarios * Hyper-V daemon_loop() Exception: Skip performed ports * Replaces neutron logging with oslo_log * Fixes oslo import notations * Fixes Hyper-V agent stateful security group rules * Bump version to 1.0.2 * Adds Hyper-V Agent configurations getter * Hyper-V: Cleanup in TestHyperVUtilsV2 * Hyper-V: Skips already performed actions * Removes unused argument from disconnect_switch_port * Fixes unorderered network mappings issue * Adds cookiecutter template * Adds Ml2 Hyper-V Mechanism Driver * Rename project to networking-hyperv * Fixes Hyper-V agent port disconnect issue * Fixes Hyper-V 2008 R2 agent VLAN Settings issue * Adds setup.py files * Updates README * Adds logging implementation * Adds i18n implementation * Decouples HyperVException fron neutron * Decouples HyperVSecurityGroupsDriver fron Neutron * Decouples HyperVNeutronAgent from Neutron * Removes main from hyperv_neutron_agent.py * Adds constant Network Types * Fixes local project imports * Adds unit tests base class * Adds Neutron Hyper-V Agent implementation * Initial commit networking-hyperv-2.0.0/CONTRIBUTING.rst0000664000567000056710000000104412677524354021033 0ustar jenkinsjenkins00000000000000If you would like to contribute to the development of OpenStack, you must follow the steps in this page: http://docs.openstack.org/infra/manual/developers.html Once those steps have been completed, changes to OpenStack should be submitted for review via the Gerrit tool, following the workflow documented at: http://docs.openstack.org/infra/manual/developers.html#development-workflow Pull requests submitted through GitHub will be ignored. Bugs should be filed on Launchpad, not GitHub: https://bugs.launchpad.net/networking-hyperv