Retire repo
This repo was created by accident, use deb-python-os-net-config instead. Needed-By: I1ac1a06931c8b6dd7c2e73620a0302c29e605f03 Change-Id: I81894aea69b9d09b0977039623c26781093a397a
This commit is contained in:
parent
9e1a613204
commit
b5418e5912
@ -1,7 +0,0 @@
|
|||||||
[run]
|
|
||||||
branch = True
|
|
||||||
source = os_net_config
|
|
||||||
omit = os_net_config/tests/*,os_net_config/openstack/*
|
|
||||||
|
|
||||||
[report]
|
|
||||||
ignore_errors = True
|
|
51
.gitignore
vendored
51
.gitignore
vendored
@ -1,51 +0,0 @@
|
|||||||
*.py[cod]
|
|
||||||
|
|
||||||
# C extensions
|
|
||||||
*.so
|
|
||||||
|
|
||||||
# Packages
|
|
||||||
*.egg
|
|
||||||
*.egg-info
|
|
||||||
dist
|
|
||||||
build
|
|
||||||
eggs
|
|
||||||
parts
|
|
||||||
bin
|
|
||||||
var
|
|
||||||
sdist
|
|
||||||
develop-eggs
|
|
||||||
.installed.cfg
|
|
||||||
lib
|
|
||||||
lib64
|
|
||||||
|
|
||||||
# Installer logs
|
|
||||||
pip-log.txt
|
|
||||||
|
|
||||||
# Unit test / coverage reports
|
|
||||||
.coverage
|
|
||||||
.tox
|
|
||||||
nosetests.xml
|
|
||||||
.testrepository
|
|
||||||
|
|
||||||
# Translations
|
|
||||||
*.mo
|
|
||||||
|
|
||||||
# Mr Developer
|
|
||||||
.mr.developer.cfg
|
|
||||||
.project
|
|
||||||
.pydevproject
|
|
||||||
|
|
||||||
# Complexity
|
|
||||||
output/*.html
|
|
||||||
output/*/index.html
|
|
||||||
|
|
||||||
# Sphinx
|
|
||||||
doc/build
|
|
||||||
|
|
||||||
# pbr generates these
|
|
||||||
AUTHORS
|
|
||||||
ChangeLog
|
|
||||||
|
|
||||||
# Editors
|
|
||||||
*~
|
|
||||||
.*.swp
|
|
@ -1,4 +0,0 @@
|
|||||||
[gerrit]
|
|
||||||
host=review.openstack.org
|
|
||||||
port=29418
|
|
||||||
project=openstack/os-net-config.git
|
|
3
.mailmap
3
.mailmap
@ -1,3 +0,0 @@
|
|||||||
# Format is:
|
|
||||||
# <preferred e-mail> <other e-mail 1>
|
|
||||||
# <preferred e-mail> <other e-mail 2>
|
|
@ -1,7 +0,0 @@
|
|||||||
[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
|
|
@ -1,16 +0,0 @@
|
|||||||
If 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/os-net-config
|
|
@ -1,4 +0,0 @@
|
|||||||
os-net-config Style Commandments
|
|
||||||
===============================================
|
|
||||||
|
|
||||||
Read the OpenStack Style Commandments http://docs.openstack.org/developer/hacking/
|
|
175
LICENSE
175
LICENSE
@ -1,175 +0,0 @@
|
|||||||
|
|
||||||
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.
|
|
@ -1,6 +0,0 @@
|
|||||||
include AUTHORS
|
|
||||||
include ChangeLog
|
|
||||||
exclude .gitignore
|
|
||||||
exclude .gitreview
|
|
||||||
|
|
||||||
global-exclude *.pyc
|
|
111
README.rst
111
README.rst
@ -1,111 +0,0 @@
|
|||||||
===============================
|
|
||||||
os-net-config
|
|
||||||
===============================
|
|
||||||
|
|
||||||
host network configuration tool
|
|
||||||
|
|
||||||
An implementation of the 'network configuration' spec @
|
|
||||||
https://review.openstack.org/#/c/97859/.
|
|
||||||
The intention is for this code to be moved under the tripleo project in due course.
|
|
||||||
|
|
||||||
* Free software: Apache license
|
|
||||||
* Documentation: http://docs.openstack.org/developer/os-net-config
|
|
||||||
* Source: http://git.openstack.org/cgit/openstack/os-net-config
|
|
||||||
* Bugs: http://bugs.launchpad.net/os-net-config
|
|
||||||
|
|
||||||
Features
|
|
||||||
--------
|
|
||||||
|
|
||||||
The core aim of this project is to allow fine grained (but extendable)
|
|
||||||
configuration of the networking parameters for a network host. The
|
|
||||||
project consists of:
|
|
||||||
|
|
||||||
* A CLI (os-net-config) which provides configuration via a YAML or JSON
|
|
||||||
file formats. By default os-net-config uses a YAML config file located
|
|
||||||
at /etc/os-net-config/config.yaml. This can be customized via the
|
|
||||||
--config-file CLI option.
|
|
||||||
|
|
||||||
* A python library which provides configuration via an object model.
|
|
||||||
|
|
||||||
YAML Config Examples
|
|
||||||
--------------------
|
|
||||||
* Configure an OVS bridge with a single attached interface (port)
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
ovs_extra:
|
|
||||||
- br-set-external-id br-ctlplane bridge-id br-ctlplane
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
|
|
||||||
..
|
|
||||||
|
|
||||||
|
|
||||||
* Configure an OVS bridge on top of an OVS bond
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: ovs_bond
|
|
||||||
name: bond1
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em2
|
|
||||||
|
|
||||||
..
|
|
||||||
|
|
||||||
* Configure a tagged VLAN interface on top of an OVS bridge
|
|
||||||
|
|
||||||
.. code-block:: yaml
|
|
||||||
|
|
||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
-
|
|
||||||
type: vlan
|
|
||||||
vlan_id: 16
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 192.0.2.1/24
|
|
||||||
|
|
||||||
..
|
|
||||||
|
|
||||||
Provider Configuration
|
|
||||||
----------------------
|
|
||||||
Providers are use to apply (implement) the desired configuration on the
|
|
||||||
host system. By default 3 providers are implemented:
|
|
||||||
|
|
||||||
* Ifcfg: persistent network config format stored in
|
|
||||||
/etc/sysconfig/network-scripts
|
|
||||||
|
|
||||||
* ENI: persistent network config format stored in /etc/network/interfaces
|
|
||||||
|
|
||||||
* iproute2: non-persistent provider which implements the config using
|
|
||||||
iproute2, vconfig, etc... (implementation in progress)
|
|
||||||
|
|
||||||
When using bin/os-net-config the provider is automatically selected based on
|
|
||||||
the host systems perferred persistent network type (ifcfg or ENI). This can
|
|
||||||
be customized via the --provider CLI option.
|
|
13
README.txt
Normal file
13
README.txt
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This project is no longer maintained.
|
||||||
|
|
||||||
|
The contents of this repository are still available in the Git
|
||||||
|
source code management system. To see the contents of this
|
||||||
|
repository before it reached its end of life, please check out the
|
||||||
|
previous commit with "git checkout HEAD^1".
|
||||||
|
|
||||||
|
Use instead the project deb-python-os-net-config at
|
||||||
|
http://git.openstack.org/cgit/openstack/deb-python-os-net-config .
|
||||||
|
|
||||||
|
For any further questions, please email
|
||||||
|
openstack-dev@lists.openstack.org or join #openstack-dev on
|
||||||
|
Freenode.
|
@ -1,75 +0,0 @@
|
|||||||
# -*- 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 = u'os-net-config'
|
|
||||||
copyright = u'2013, OpenStack Foundation'
|
|
||||||
|
|
||||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
|
||||||
add_function_parentheses = True
|
|
||||||
|
|
||||||
# If true, the current module name will be prepended to all description
|
|
||||||
# unit titles (such as .. function::).
|
|
||||||
add_module_names = True
|
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
|
||||||
pygments_style = 'sphinx'
|
|
||||||
|
|
||||||
# -- Options for HTML output --------------------------------------------------
|
|
||||||
|
|
||||||
# The theme to use for HTML and HTML Help pages. Major themes that come with
|
|
||||||
# Sphinx are currently 'default' and 'sphinxdoc'.
|
|
||||||
# html_theme_path = ["."]
|
|
||||||
# html_theme = '_theme'
|
|
||||||
# html_static_path = ['static']
|
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
|
||||||
htmlhelp_basename = '%sdoc' % project
|
|
||||||
|
|
||||||
# Grouping the document tree into LaTeX files. List of tuples
|
|
||||||
# (source start file, target name, title, author, documentclass
|
|
||||||
# [howto/manual]).
|
|
||||||
latex_documents = [
|
|
||||||
('index',
|
|
||||||
'%s.tex' % project,
|
|
||||||
u'%s Documentation' % project,
|
|
||||||
u'OpenStack Foundation', 'manual'),
|
|
||||||
]
|
|
||||||
|
|
||||||
# Example configuration for intersphinx: refer to the Python standard library.
|
|
||||||
#intersphinx_mapping = {'http://docs.python.org/': None}
|
|
@ -1,4 +0,0 @@
|
|||||||
============
|
|
||||||
Contributing
|
|
||||||
============
|
|
||||||
.. include:: ../../CONTRIBUTING.rst
|
|
@ -1,24 +0,0 @@
|
|||||||
.. os-net-config 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 os-net-config's documentation!
|
|
||||||
========================================================
|
|
||||||
|
|
||||||
Contents:
|
|
||||||
|
|
||||||
.. toctree::
|
|
||||||
:maxdepth: 2
|
|
||||||
|
|
||||||
readme
|
|
||||||
installation
|
|
||||||
usage
|
|
||||||
contributing
|
|
||||||
|
|
||||||
Indices and tables
|
|
||||||
==================
|
|
||||||
|
|
||||||
* :ref:`genindex`
|
|
||||||
* :ref:`modindex`
|
|
||||||
* :ref:`search`
|
|
@ -1,12 +0,0 @@
|
|||||||
============
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
At the command line::
|
|
||||||
|
|
||||||
$ pip install os-net-config
|
|
||||||
|
|
||||||
Or, if you have virtualenvwrapper installed::
|
|
||||||
|
|
||||||
$ mkvirtualenv os-net-config
|
|
||||||
$ pip install os-net-config
|
|
@ -1 +0,0 @@
|
|||||||
.. include:: ../../README.rst
|
|
@ -1,7 +0,0 @@
|
|||||||
========
|
|
||||||
Usage
|
|
||||||
========
|
|
||||||
|
|
||||||
To use os-net-config in a project::
|
|
||||||
|
|
||||||
import os_net_config
|
|
@ -1,19 +0,0 @@
|
|||||||
{ "network_config": [
|
|
||||||
{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-ctlplane",
|
|
||||||
"use_dhcp": "true",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "ovs_bond",
|
|
||||||
"name": "bond1",
|
|
||||||
"use_dhcp": "true",
|
|
||||||
"members": [
|
|
||||||
{ "type": "interface", "name": "em1" },
|
|
||||||
{ "type": "interface", "name": "em2" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: ovs_bond
|
|
||||||
name: bond1
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em2
|
|
@ -1,23 +0,0 @@
|
|||||||
# Example showing use of the optional nicN abstraction
|
|
||||||
# for device naming, which defaults to an ordered
|
|
||||||
# translation to biodev names based on which interfaces
|
|
||||||
# are active on the system.
|
|
||||||
# Optionally the default mapping may be overriden by
|
|
||||||
# a mapping file via the -m option.
|
|
||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: ovs_bond
|
|
||||||
name: bond1
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: nic1
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: nic2
|
|
@ -1,17 +0,0 @@
|
|||||||
{ "network_config": [
|
|
||||||
{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-ctlplane",
|
|
||||||
"ovs_extra": [
|
|
||||||
"br-set-external-id br-ctlplane bridge-id br-ctlplane"
|
|
||||||
],
|
|
||||||
"use_dhcp": "true",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
ovs_extra:
|
|
||||||
- br-set-external-id br-ctlplane bridge-id br-ctlplane
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
@ -1,25 +0,0 @@
|
|||||||
{ "network_config": [
|
|
||||||
{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-ctlplane",
|
|
||||||
"use_dhcp": "true",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "vlan",
|
|
||||||
"vlan_id": 16,
|
|
||||||
"addresses": [{
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"routes": [{
|
|
||||||
"next_hop": "192.0.2.1",
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
-
|
|
||||||
type: vlan
|
|
||||||
vlan_id: 16
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 192.0.2.1/24
|
|
||||||
routes:
|
|
||||||
-
|
|
||||||
next_hop: 192.0.2.1
|
|
||||||
ip_netmask: 192.0.2.1/24
|
|
@ -1,26 +0,0 @@
|
|||||||
{"network_config": [
|
|
||||||
{
|
|
||||||
"type": "ib_interface",
|
|
||||||
"name": "ib0",
|
|
||||||
"use_dhcp": false,
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "0.0.0.0/0",
|
|
||||||
"next_hop": "192.0.2.254",
|
|
||||||
"default": "true"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "ib_interface",
|
|
||||||
"name": "ib1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"defroute": no
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ib_interface
|
|
||||||
name: ib0
|
|
||||||
use_dhcp: false
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 192.0.2.1/24
|
|
||||||
routes:
|
|
||||||
-
|
|
||||||
ip_netmask: 0.0.0.0/0
|
|
||||||
next_hop: 192.0.2.254
|
|
||||||
default: true
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: ib1
|
|
||||||
use_dhcp: true
|
|
||||||
defroute: no
|
|
@ -1,26 +0,0 @@
|
|||||||
{"network_config": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"use_dhcp": false,
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "0.0.0.0/0",
|
|
||||||
"next_hop": "192.0.2.254",
|
|
||||||
"default": "true"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em2",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"defroute": no
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
use_dhcp: false
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 192.0.2.1/24
|
|
||||||
routes:
|
|
||||||
-
|
|
||||||
ip_netmask: 0.0.0.0/0
|
|
||||||
next_hop: 192.0.2.254
|
|
||||||
default: true
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em2
|
|
||||||
use_dhcp: true
|
|
||||||
defroute: no
|
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"network_config": [
|
|
||||||
{
|
|
||||||
"type": "ivs_bridge",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "ivs_interface",
|
|
||||||
"name": "api",
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "172.16.2.7/24"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vlan_id": 201
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "ivs_interface",
|
|
||||||
"name": "storage",
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "172.16.1.6/24"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vlan_id": 202
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ivs_bridge
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: nic2
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: nic3
|
|
||||||
-
|
|
||||||
type: ivs_interface
|
|
||||||
name: api
|
|
||||||
vlan_id: 201
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 172.16.2.7/24
|
|
||||||
-
|
|
||||||
type: ivs_interface
|
|
||||||
name: storage
|
|
||||||
vlan_id: 202
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 172.16.1.6/24
|
|
@ -1,13 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: linux_bond
|
|
||||||
name: bond1
|
|
||||||
use_dhcp: true
|
|
||||||
bonding_options: "mode=active-backup"
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em2
|
|
@ -1,9 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: linux_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
use_dhcp: true
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
@ -1,12 +0,0 @@
|
|||||||
# This can be used with the -m option to override the
|
|
||||||
# default mapping of the nicN aliases in configs
|
|
||||||
# The mapping can specify either a device name or a mac address
|
|
||||||
# If --persist-mapping is specified, we write the device aliases
|
|
||||||
# config instead of the system names, e.g we actually configure
|
|
||||||
# nic1 intead of em3. This is probably best used with --cleanup
|
|
||||||
# to remove the stale configs e.g for em3
|
|
||||||
interface_mapping:
|
|
||||||
nic1: em3
|
|
||||||
nic2: em1
|
|
||||||
nic3: 12:34:56:de:f0:12
|
|
||||||
nic4: 12:34:56:78:9a:bc
|
|
@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"network_config": [
|
|
||||||
{
|
|
||||||
"type": "nfvswitch_bridge",
|
|
||||||
"cpus": "2,3,4,5",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic2",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic3"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "nfvswitch_internal",
|
|
||||||
"name": "api",
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "172.16.2.7/24"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vlan_id": 201
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "nfvswitch_internal",
|
|
||||||
"name": "storage",
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "172.16.1.6/24"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"vlan_id": 202
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: nfvswitch_bridge
|
|
||||||
cpus: "2,3,4,5"
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: nic2
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: nic3
|
|
||||||
-
|
|
||||||
type: nfvswitch_internal
|
|
||||||
name: api
|
|
||||||
vlan_id: 201
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 172.16.2.7/24
|
|
||||||
-
|
|
||||||
type: nfvswitch_internal
|
|
||||||
name: storage
|
|
||||||
vlan_id: 202
|
|
||||||
addresses:
|
|
||||||
-
|
|
||||||
ip_netmask: 172.16.1.6/24
|
|
@ -1,37 +0,0 @@
|
|||||||
{
|
|
||||||
"network_config": [
|
|
||||||
{
|
|
||||||
"name": "br-ctlplane",
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"mtu": 1500,
|
|
||||||
"primary": "true",
|
|
||||||
"name": "eth1",
|
|
||||||
"type": "interface"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ovs_extra": [
|
|
||||||
"br-set-external-id br-ctlplane bridge-id br-ctlplane"
|
|
||||||
],
|
|
||||||
"mtu": 1500,
|
|
||||||
"addresses": [
|
|
||||||
{
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "br_pub-patch",
|
|
||||||
"type": "ovs_patch_port",
|
|
||||||
"bridge_name": "br-ctlplane",
|
|
||||||
"peer": "br-ctlplane-patch"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "br-ctlplane-patch",
|
|
||||||
"type": "ovs_patch_port",
|
|
||||||
"bridge_name": "br_pub",
|
|
||||||
"peer": "br_pub-patch"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
network_config:
|
|
||||||
-
|
|
||||||
type: ovs_bridge
|
|
||||||
name: br-ctlplane
|
|
||||||
mtu: 1500
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: eth1
|
|
||||||
# force the MAC address of the bridge to this interface
|
|
||||||
primary: true
|
|
||||||
mtu: 1500
|
|
||||||
ovs_extra: "br-set-external-id br-ctlplane bridge-id br-ctlplane"
|
|
||||||
-
|
|
||||||
type: ovs_patch_port
|
|
||||||
name: br_pub-patch
|
|
||||||
bridge_name: br-ctlplane
|
|
||||||
peer: br-ctlplane-patch
|
|
||||||
-
|
|
||||||
type: ovs_patch_port
|
|
||||||
name: br-ctlplane-patch
|
|
||||||
bridge_name: br_pub
|
|
||||||
peer: br_pub-patch
|
|
@ -1,13 +0,0 @@
|
|||||||
{ "network_config": [
|
|
||||||
{
|
|
||||||
"type": "team",
|
|
||||||
"name": "team1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"bonding_options": "{\"runner\": {\"name\": \"activebackup\"}}",
|
|
||||||
"members": [
|
|
||||||
{ "type": "interface", "name": "em1", "primary": true },
|
|
||||||
{ "type": "interface", "name": "em2" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
# Config for bonding with teamd. Bonding options are provided as a JSON
|
|
||||||
# string. The following runners are available in teamd: broadcast,
|
|
||||||
# roundrobin, activebackup, loadbalance, and lacp.
|
|
||||||
# Please see the teamd.conf(5) man page for more information.
|
|
||||||
network_config:
|
|
||||||
-
|
|
||||||
type: team
|
|
||||||
name: team1
|
|
||||||
use_dhcp: true
|
|
||||||
bonding_options: '{"runner": {"name": "activebackup"}}'
|
|
||||||
members:
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em1
|
|
||||||
primary: true
|
|
||||||
-
|
|
||||||
type: interface
|
|
||||||
name: em2
|
|
@ -1,233 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014-2015 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils
|
|
||||||
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class NotImplemented(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class NetConfig(object):
|
|
||||||
"""Common network config methods class."""
|
|
||||||
|
|
||||||
def __init__(self, noop=False, root_dir=''):
|
|
||||||
self.noop = noop
|
|
||||||
self.log_prefix = "NOOP: " if noop else ""
|
|
||||||
self.root_dir = root_dir
|
|
||||||
|
|
||||||
def add_object(self, obj):
|
|
||||||
"""Convenience method to add any type of object to the network config.
|
|
||||||
|
|
||||||
See objects.py.
|
|
||||||
|
|
||||||
:param obj: The object to add.
|
|
||||||
"""
|
|
||||||
if isinstance(obj, objects.Interface):
|
|
||||||
self.add_interface(obj)
|
|
||||||
elif isinstance(obj, objects.Vlan):
|
|
||||||
self.add_vlan(obj)
|
|
||||||
elif isinstance(obj, objects.IvsInterface):
|
|
||||||
self.add_ivs_interface(obj)
|
|
||||||
elif isinstance(obj, objects.NfvswitchInternal):
|
|
||||||
self.add_nfvswitch_internal(obj)
|
|
||||||
elif isinstance(obj, objects.OvsBridge):
|
|
||||||
self.add_bridge(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.LinuxBridge):
|
|
||||||
self.add_linux_bridge(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.IvsBridge):
|
|
||||||
self.add_ivs_bridge(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.NfvswitchBridge):
|
|
||||||
self.add_nfvswitch_bridge(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.OvsBond):
|
|
||||||
self.add_bond(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.LinuxBond):
|
|
||||||
self.add_linux_bond(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.LinuxTeam):
|
|
||||||
self.add_linux_team(obj)
|
|
||||||
for member in obj.members:
|
|
||||||
self.add_object(member)
|
|
||||||
elif isinstance(obj, objects.OvsTunnel):
|
|
||||||
self.add_ovs_tunnel(obj)
|
|
||||||
elif isinstance(obj, objects.OvsPatchPort):
|
|
||||||
self.add_ovs_patch_port(obj)
|
|
||||||
elif isinstance(obj, objects.IbInterface):
|
|
||||||
self.add_ib_interface(obj)
|
|
||||||
|
|
||||||
def add_interface(self, interface):
|
|
||||||
"""Add an Interface object to the net config object.
|
|
||||||
|
|
||||||
:param interface: The Interface object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_interface is not implemented.")
|
|
||||||
|
|
||||||
def add_vlan(self, vlan):
|
|
||||||
"""Add a Vlan object to the net config object.
|
|
||||||
|
|
||||||
:param vlan: The vlan object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_vlan is not implemented.")
|
|
||||||
|
|
||||||
def add_bridge(self, bridge):
|
|
||||||
"""Add an OvsBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The OvsBridge object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_bridge is not implemented.")
|
|
||||||
|
|
||||||
def add_linux_bridge(self, bridge):
|
|
||||||
"""Add a LinuxBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The LinuxBridge object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_linux_bridge is not implemented.")
|
|
||||||
|
|
||||||
def add_ivs_bridge(self, bridge):
|
|
||||||
"""Add a IvsBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The IvsBridge object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_ivs_bridge is not implemented.")
|
|
||||||
|
|
||||||
def add_nfvswitch_bridge(self, bridge):
|
|
||||||
"""Add a NfvswitchBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The NfvswitchBridge object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_nfvswitch_bridge is not implemented.")
|
|
||||||
|
|
||||||
def add_bond(self, bond):
|
|
||||||
"""Add an OvsBond object to the net config object.
|
|
||||||
|
|
||||||
:param bond: The OvsBond object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_bond is not implemented.")
|
|
||||||
|
|
||||||
def add_linux_bond(self, bond):
|
|
||||||
"""Add a LinuxBond object to the net config object.
|
|
||||||
|
|
||||||
:param bond: The LinuxBond object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_linux_bond is not implemented.")
|
|
||||||
|
|
||||||
def add_linux_team(self, team):
|
|
||||||
"""Add a LinuxTeam object to the net config object.
|
|
||||||
|
|
||||||
:param team: The LinuxTeam object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_linux_team is not implemented.")
|
|
||||||
|
|
||||||
def add_ovs_tunnel(self, tunnel):
|
|
||||||
"""Add a OvsTunnel object to the net config object.
|
|
||||||
|
|
||||||
:param tunnel: The OvsTunnel object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_ovs_tunnel is not implemented.")
|
|
||||||
|
|
||||||
def add_ovs_patch_port(self, ovs_patch_port):
|
|
||||||
"""Add a OvsPatchPort object to the net config object.
|
|
||||||
|
|
||||||
:param ovs_patch_port: The OvsPatchPort object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_ovs_patch_port is not implemented.")
|
|
||||||
|
|
||||||
def add_ib_interface(self, ib_interface):
|
|
||||||
"""Add an InfiniBand Interface object to the net config object.
|
|
||||||
|
|
||||||
:param interface: The InfiniBand Interface object to add.
|
|
||||||
"""
|
|
||||||
raise NotImplemented("add_ib_interface is not implemented.")
|
|
||||||
|
|
||||||
def apply(self, cleanup=False):
|
|
||||||
"""Apply the network configuration.
|
|
||||||
|
|
||||||
:param cleanup: A boolean which indicates whether any undefined
|
|
||||||
(existing but not present in the object model) interfaces
|
|
||||||
should be disabled and deleted.
|
|
||||||
:returns: a dict of the format: filename/data which contains info
|
|
||||||
for each file that was changed (or would be changed if in --noop
|
|
||||||
mode).
|
|
||||||
"""
|
|
||||||
raise NotImplemented("apply is not implemented.")
|
|
||||||
|
|
||||||
def execute(self, msg, cmd, *args, **kwargs):
|
|
||||||
"""Print a message and run a command.
|
|
||||||
|
|
||||||
Print a message and run a command with processutils
|
|
||||||
in noop mode, this just prints a message.
|
|
||||||
"""
|
|
||||||
logger.info('%s%s' % (self.log_prefix, msg))
|
|
||||||
if not self.noop:
|
|
||||||
processutils.execute(cmd, *args, **kwargs)
|
|
||||||
|
|
||||||
def write_config(self, filename, data, msg=None):
|
|
||||||
msg = msg or "Writing config %s" % filename
|
|
||||||
logger.info('%s%s' % (self.log_prefix, msg))
|
|
||||||
if not self.noop:
|
|
||||||
utils.write_config(filename, data)
|
|
||||||
|
|
||||||
def remove_config(self, filename, msg=None):
|
|
||||||
msg = msg or "Removing config %s" % filename
|
|
||||||
logger.info('%s%s' % (self.log_prefix, msg))
|
|
||||||
if not self.noop:
|
|
||||||
os.remove(filename)
|
|
||||||
|
|
||||||
def ifdown(self, interface, iftype='interface'):
|
|
||||||
msg = 'running ifdown on %s: %s' % (iftype, interface)
|
|
||||||
self.execute(msg, '/sbin/ifdown', interface, check_exit_code=False)
|
|
||||||
|
|
||||||
def ifup(self, interface, iftype='interface'):
|
|
||||||
msg = 'running ifup on %s: %s' % (iftype, interface)
|
|
||||||
self.execute(msg, '/sbin/ifup', interface)
|
|
||||||
|
|
||||||
def ifrename(self, oldname, newname):
|
|
||||||
msg = 'renaming %s to %s: ' % (oldname, newname)
|
|
||||||
# ifdown isn't enough when renaming, we need the link down
|
|
||||||
for name in (oldname, newname):
|
|
||||||
if utils._is_active_nic(name):
|
|
||||||
self.execute(msg, '/sbin/ip',
|
|
||||||
'link', 'set', 'dev', name, 'down')
|
|
||||||
self.execute(msg, '/sbin/ip',
|
|
||||||
'link', 'set', 'dev', name, 'link', 'down')
|
|
||||||
self.execute(msg, '/sbin/ip',
|
|
||||||
'link', 'set', 'dev', oldname, 'name', newname)
|
|
||||||
self.execute(msg, '/sbin/ip',
|
|
||||||
'link', 'set', 'dev', newname, 'up')
|
|
||||||
|
|
||||||
def ovs_appctl(self, action, *parameters):
|
|
||||||
msg = 'Running ovs-appctl %s %s' % (action, parameters)
|
|
||||||
self.execute(msg, '/bin/ovs-appctl', action, *parameters)
|
|
@ -1,201 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014-2015 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import yaml
|
|
||||||
|
|
||||||
from os_net_config import impl_eni
|
|
||||||
from os_net_config import impl_ifcfg
|
|
||||||
from os_net_config import impl_iproute
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config import version
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_opts(argv):
|
|
||||||
parser = argparse.ArgumentParser(
|
|
||||||
description='Configure host network interfaces using a JSON'
|
|
||||||
' config file format.')
|
|
||||||
parser.add_argument('-c', '--config-file', metavar='CONFIG_FILE',
|
|
||||||
help="""path to the configuration file.""",
|
|
||||||
default='/etc/os-net-config/config.yaml')
|
|
||||||
parser.add_argument('-m', '--mapping-file', metavar='MAPPING_FILE',
|
|
||||||
help="""path to the interface mapping file.""",
|
|
||||||
default='/etc/os-net-config/mapping.yaml')
|
|
||||||
parser.add_argument('-p', '--provider', metavar='PROVIDER',
|
|
||||||
help="""The provider to use."""
|
|
||||||
"""One of: ifcfg, eni, iproute.""",
|
|
||||||
default=None)
|
|
||||||
parser.add_argument('-r', '--root-dir', metavar='ROOT_DIR',
|
|
||||||
help="""The root directory of the filesystem.""",
|
|
||||||
default='')
|
|
||||||
parser.add_argument('--detailed-exit-codes',
|
|
||||||
action='store_true',
|
|
||||||
help="""Enable detailed exit codes. """
|
|
||||||
"""If enabled an exit code of '2' means """
|
|
||||||
"""that files were modified."""
|
|
||||||
"""Disabled by default.""",
|
|
||||||
default=False)
|
|
||||||
parser.add_argument(
|
|
||||||
'-d', '--debug',
|
|
||||||
dest="debug",
|
|
||||||
action='store_true',
|
|
||||||
help="Print debugging output.",
|
|
||||||
required=False)
|
|
||||||
parser.add_argument(
|
|
||||||
'-v', '--verbose',
|
|
||||||
dest="verbose",
|
|
||||||
action='store_true',
|
|
||||||
help="Print verbose output.",
|
|
||||||
required=False)
|
|
||||||
|
|
||||||
parser.add_argument('--version', action='version',
|
|
||||||
version=version.version_info.version_string())
|
|
||||||
parser.add_argument(
|
|
||||||
'--noop',
|
|
||||||
dest="noop",
|
|
||||||
action='store_true',
|
|
||||||
help="Return the configuration commands, without applying them.",
|
|
||||||
required=False)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'--no-activate',
|
|
||||||
dest="no_activate",
|
|
||||||
action='store_true',
|
|
||||||
help="Install the configuration but don't start/stop interfaces.",
|
|
||||||
required=False)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'--cleanup',
|
|
||||||
dest="cleanup",
|
|
||||||
action='store_true',
|
|
||||||
help="Cleanup unconfigured interfaces.",
|
|
||||||
required=False)
|
|
||||||
|
|
||||||
parser.add_argument(
|
|
||||||
'--persist-mapping',
|
|
||||||
dest="persist_mapping",
|
|
||||||
action='store_true',
|
|
||||||
help="Make aliases defined in the mapping file permanent "
|
|
||||||
"(WARNING, permanently renames nics).",
|
|
||||||
required=False)
|
|
||||||
|
|
||||||
opts = parser.parse_args(argv[1:])
|
|
||||||
|
|
||||||
return opts
|
|
||||||
|
|
||||||
|
|
||||||
def configure_logger(verbose=False, debug=False):
|
|
||||||
LOG_FORMAT = '[%(asctime)s] [%(levelname)s] %(message)s'
|
|
||||||
DATE_FORMAT = '%Y/%m/%d %I:%M:%S %p'
|
|
||||||
log_level = logging.WARN
|
|
||||||
|
|
||||||
if debug:
|
|
||||||
log_level = logging.DEBUG
|
|
||||||
elif verbose:
|
|
||||||
log_level = logging.INFO
|
|
||||||
|
|
||||||
logging.basicConfig(format=LOG_FORMAT, datefmt=DATE_FORMAT,
|
|
||||||
level=log_level)
|
|
||||||
|
|
||||||
|
|
||||||
def main(argv=sys.argv):
|
|
||||||
opts = parse_opts(argv)
|
|
||||||
configure_logger(opts.verbose, opts.debug)
|
|
||||||
logger.info('Using config file at: %s' % opts.config_file)
|
|
||||||
if opts.mapping_file:
|
|
||||||
logger.info('Using mapping file at: %s' % opts.mapping_file)
|
|
||||||
iface_array = []
|
|
||||||
|
|
||||||
provider = None
|
|
||||||
if opts.provider:
|
|
||||||
if opts.provider == 'ifcfg':
|
|
||||||
provider = impl_ifcfg.IfcfgNetConfig(noop=opts.noop,
|
|
||||||
root_dir=opts.root_dir)
|
|
||||||
elif opts.provider == 'eni':
|
|
||||||
provider = impl_eni.ENINetConfig(noop=opts.noop,
|
|
||||||
root_dir=opts.root_dir)
|
|
||||||
elif opts.provider == 'iproute':
|
|
||||||
provider = impl_iproute.IPRouteNetConfig(noop=opts.noop,
|
|
||||||
root_dir=opts.root_dir)
|
|
||||||
else:
|
|
||||||
logger.error('Invalid provider specified.')
|
|
||||||
return 1
|
|
||||||
else:
|
|
||||||
if os.path.exists('%s/etc/sysconfig/network-scripts/' % opts.root_dir):
|
|
||||||
provider = impl_ifcfg.IfcfgNetConfig(noop=opts.noop,
|
|
||||||
root_dir=opts.root_dir)
|
|
||||||
elif os.path.exists('%s/etc/network/' % opts.root_dir):
|
|
||||||
provider = impl_eni.ENINetConfig(noop=opts.noop,
|
|
||||||
root_dir=opts.root_dir)
|
|
||||||
else:
|
|
||||||
logger.error('Unable to set provider for this operating system.')
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Read config file containing network configs to apply
|
|
||||||
if os.path.exists(opts.config_file):
|
|
||||||
with open(opts.config_file) as cf:
|
|
||||||
iface_array = yaml.load(cf.read()).get("network_config")
|
|
||||||
logger.debug('network_config JSON: %s' % str(iface_array))
|
|
||||||
else:
|
|
||||||
logger.error('No config file exists at: %s' % opts.config_file)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
if not isinstance(iface_array, list):
|
|
||||||
logger.error('No interfaces defined in config: %s' % opts.config_file)
|
|
||||||
return 1
|
|
||||||
|
|
||||||
# Read the interface mapping file, if it exists
|
|
||||||
# This allows you to override the default network naming abstraction
|
|
||||||
# mappings by specifying a specific nicN->name or nicN->MAC mapping
|
|
||||||
if os.path.exists(opts.mapping_file):
|
|
||||||
with open(opts.mapping_file) as cf:
|
|
||||||
iface_map = yaml.load(cf.read())
|
|
||||||
iface_mapping = iface_map.get("interface_mapping")
|
|
||||||
logger.debug('interface_mapping JSON: %s' % str(iface_mapping))
|
|
||||||
persist_mapping = opts.persist_mapping
|
|
||||||
logger.debug('persist_mapping: %s' % persist_mapping)
|
|
||||||
else:
|
|
||||||
iface_mapping = None
|
|
||||||
persist_mapping = False
|
|
||||||
|
|
||||||
for iface_json in iface_array:
|
|
||||||
iface_json.update({'nic_mapping': iface_mapping})
|
|
||||||
iface_json.update({'persist_mapping': persist_mapping})
|
|
||||||
obj = objects.object_from_json(iface_json)
|
|
||||||
provider.add_object(obj)
|
|
||||||
files_changed = provider.apply(cleanup=opts.cleanup,
|
|
||||||
activate=not opts.no_activate)
|
|
||||||
if opts.noop:
|
|
||||||
for location, data in files_changed.iteritems():
|
|
||||||
print("File: %s\n" % location)
|
|
||||||
print(data)
|
|
||||||
print("----")
|
|
||||||
|
|
||||||
if opts.detailed_exit_codes and len(files_changed) > 0:
|
|
||||||
return 2
|
|
||||||
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
sys.exit(main(sys.argv))
|
|
@ -1,241 +0,0 @@
|
|||||||
# -*- Coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014-2015 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
|
|
||||||
import netaddr
|
|
||||||
import os_net_config
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO(?): should move to interfaces.d
|
|
||||||
def _network_config_path(prefix=''):
|
|
||||||
return prefix + "/etc/network/interfaces"
|
|
||||||
|
|
||||||
|
|
||||||
class ENINetConfig(os_net_config.NetConfig):
|
|
||||||
"""Debian/Ubuntu implementation for network config
|
|
||||||
|
|
||||||
Configure iface/bridge/routes using debian/ubuntu
|
|
||||||
/etc/network/interfaces format.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, noop=False, root_dir=''):
|
|
||||||
super(ENINetConfig, self).__init__(noop, root_dir)
|
|
||||||
self.interfaces = {}
|
|
||||||
self.routes = {}
|
|
||||||
self.bridges = {}
|
|
||||||
logger.info('ENI net config provider created.')
|
|
||||||
|
|
||||||
def _add_common(self, interface, static_addr=None, ip_version=4):
|
|
||||||
|
|
||||||
ovs_extra = []
|
|
||||||
data = ""
|
|
||||||
address_data = ""
|
|
||||||
if static_addr:
|
|
||||||
address_data += " address %s\n" % static_addr.ip
|
|
||||||
if ip_version == 6:
|
|
||||||
address_data += " netmask %s\n" % static_addr.prefixlen
|
|
||||||
else:
|
|
||||||
address_data += " netmask %s\n" % static_addr.netmask
|
|
||||||
else:
|
|
||||||
v4_addresses = interface.v4_addresses()
|
|
||||||
if v4_addresses:
|
|
||||||
for v4_address in v4_addresses:
|
|
||||||
data += self._add_common(interface, v4_address)
|
|
||||||
|
|
||||||
v6_addresses = interface.v6_addresses()
|
|
||||||
if v6_addresses:
|
|
||||||
for v6_address in v6_addresses:
|
|
||||||
data += self._add_common(interface, v6_address, 6)
|
|
||||||
|
|
||||||
if data:
|
|
||||||
return data
|
|
||||||
|
|
||||||
if isinstance(interface, objects.Vlan):
|
|
||||||
_iface = "iface vlan%i " % interface.vlan_id
|
|
||||||
else:
|
|
||||||
_iface = "iface %s " % interface.name
|
|
||||||
if static_addr and static_addr.version == 6:
|
|
||||||
_iface += "inet6 "
|
|
||||||
else:
|
|
||||||
_iface += "inet "
|
|
||||||
if interface.use_dhcp:
|
|
||||||
_iface += "dhcp\n"
|
|
||||||
elif interface.addresses:
|
|
||||||
_iface += "static\n"
|
|
||||||
else:
|
|
||||||
_iface += "manual\n"
|
|
||||||
if isinstance(interface, objects.OvsBridge):
|
|
||||||
data += "auto %s\n" % interface.name
|
|
||||||
data += "allow-ovs %s\n" % interface.name
|
|
||||||
data += _iface
|
|
||||||
data += address_data
|
|
||||||
data += " ovs_type OVSBridge\n"
|
|
||||||
if interface.members:
|
|
||||||
data += " ovs_ports"
|
|
||||||
for i in interface.members:
|
|
||||||
data += " %s" % i.name
|
|
||||||
data += "\n"
|
|
||||||
for mem in interface.members:
|
|
||||||
if isinstance(mem, objects.Interface):
|
|
||||||
data += " pre-up ip addr flush dev %s\n" % mem.name
|
|
||||||
if interface.primary_interface_name:
|
|
||||||
mac = utils.interface_mac(interface.primary_interface_name)
|
|
||||||
ovs_extra.append("set bridge %s other-config:hwaddr=%s" %
|
|
||||||
(interface.name, mac))
|
|
||||||
ovs_extra.extend(interface.ovs_extra)
|
|
||||||
elif interface.ovs_port:
|
|
||||||
if isinstance(interface, objects.Vlan):
|
|
||||||
data += "auto vlan%i\n" % interface.vlan_id
|
|
||||||
data += "allow-%s vlan%i\n" % (interface.bridge_name,
|
|
||||||
interface.vlan_id)
|
|
||||||
data += _iface
|
|
||||||
data += address_data
|
|
||||||
data += " ovs_bridge %s\n" % interface.bridge_name
|
|
||||||
data += " ovs_type OVSIntPort\n"
|
|
||||||
data += " ovs_options tag=%s\n" % interface.vlan_id
|
|
||||||
|
|
||||||
else:
|
|
||||||
data += "auto %s\n" % interface.name
|
|
||||||
data += "allow-%s %s\n" % (interface.bridge_name,
|
|
||||||
interface.name)
|
|
||||||
data += _iface
|
|
||||||
data += address_data
|
|
||||||
data += " ovs_bridge %s\n" % interface.bridge_name
|
|
||||||
data += " ovs_type OVSPort\n"
|
|
||||||
elif isinstance(interface, objects.Vlan):
|
|
||||||
data += "auto vlan%i\n" % interface.vlan_id
|
|
||||||
data += _iface
|
|
||||||
data += address_data
|
|
||||||
data += " vlan-raw-device %s\n" % interface.device
|
|
||||||
else:
|
|
||||||
data += "auto %s\n" % interface.name
|
|
||||||
data += _iface
|
|
||||||
data += address_data
|
|
||||||
if interface.mtu:
|
|
||||||
data += " mtu %i\n" % interface.mtu
|
|
||||||
|
|
||||||
if interface.hwaddr:
|
|
||||||
raise NotImplemented("hwaddr is not implemented.")
|
|
||||||
|
|
||||||
if ovs_extra:
|
|
||||||
data += " ovs_extra %s\n" % " -- ".join(ovs_extra)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def add_interface(self, interface):
|
|
||||||
"""Add an Interface object to the net config object.
|
|
||||||
|
|
||||||
:param interface: The Interface object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding interface: %s' % interface.name)
|
|
||||||
data = self._add_common(interface)
|
|
||||||
logger.debug('interface data: %s' % data)
|
|
||||||
self.interfaces[interface.name] = data
|
|
||||||
if interface.routes:
|
|
||||||
self._add_routes(interface.name, interface.routes)
|
|
||||||
|
|
||||||
def add_bridge(self, bridge):
|
|
||||||
"""Add an OvsBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The OvsBridge object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding bridge: %s' % bridge.name)
|
|
||||||
data = self._add_common(bridge)
|
|
||||||
logger.debug('bridge data: %s' % data)
|
|
||||||
self.bridges[bridge.name] = data
|
|
||||||
if bridge.routes:
|
|
||||||
self._add_routes(bridge.name, bridge.routes)
|
|
||||||
|
|
||||||
def add_vlan(self, vlan):
|
|
||||||
"""Add a Vlan object to the net config object.
|
|
||||||
|
|
||||||
:param vlan: The vlan object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding vlan: %s' % vlan.name)
|
|
||||||
data = self._add_common(vlan)
|
|
||||||
logger.debug('vlan data: %s' % data)
|
|
||||||
self.interfaces[vlan.name] = data
|
|
||||||
if vlan.routes:
|
|
||||||
self._add_routes(vlan.name, vlan.routes)
|
|
||||||
|
|
||||||
def _add_routes(self, interface_name, routes=[]):
|
|
||||||
logger.info('adding custom route for interface: %s' % interface_name)
|
|
||||||
data = ""
|
|
||||||
for route in routes:
|
|
||||||
if route.default and not route.ip_netmask:
|
|
||||||
rt = netaddr.IPNetwork("0.0.0.0/0")
|
|
||||||
else:
|
|
||||||
rt = netaddr.IPNetwork(route.ip_netmask)
|
|
||||||
data += "up route add -net %s netmask %s gw %s\n" % (
|
|
||||||
str(rt.ip), str(rt.netmask), route.next_hop)
|
|
||||||
data += "down route del -net %s netmask %s gw %s\n" % (
|
|
||||||
str(rt.ip), str(rt.netmask), route.next_hop)
|
|
||||||
self.routes[interface_name] = data
|
|
||||||
logger.debug('route data: %s' % self.routes[interface_name])
|
|
||||||
|
|
||||||
def apply(self, cleanup=False, activate=True):
|
|
||||||
"""Apply the network configuration.
|
|
||||||
|
|
||||||
:param cleanup: A boolean which indicates whether any undefined
|
|
||||||
(existing but not present in the object model) interface
|
|
||||||
should be disabled and deleted.
|
|
||||||
:param activate: A boolean which indicates if the config should
|
|
||||||
be activated by stopping/starting interfaces
|
|
||||||
:returns: a dict of the format: filename/data which contains info
|
|
||||||
for each file that was changed (or would be changed if in --noop
|
|
||||||
mode).
|
|
||||||
Note the noop mode is set via the constructor noop boolean
|
|
||||||
"""
|
|
||||||
new_config = ""
|
|
||||||
|
|
||||||
# write out bridges first. This ensures that an ifup -a
|
|
||||||
# on reboot brings them up first
|
|
||||||
for bridge_name, bridge_data in self.bridges.iteritems():
|
|
||||||
route_data = self.routes.get(bridge_name)
|
|
||||||
bridge_data += (route_data or '')
|
|
||||||
new_config += bridge_data
|
|
||||||
|
|
||||||
for interface_name, iface_data in self.interfaces.iteritems():
|
|
||||||
route_data = self.routes.get(interface_name)
|
|
||||||
iface_data += (route_data or '')
|
|
||||||
new_config += iface_data
|
|
||||||
|
|
||||||
if utils.diff(_network_config_path(self.root_dir), new_config):
|
|
||||||
if activate:
|
|
||||||
for interface in self.interfaces.keys():
|
|
||||||
self.ifdown(interface)
|
|
||||||
|
|
||||||
for bridge in self.bridges.keys():
|
|
||||||
self.ifdown(bridge, iftype='bridge')
|
|
||||||
|
|
||||||
self.write_config(_network_config_path(self.root_dir), new_config)
|
|
||||||
|
|
||||||
if activate:
|
|
||||||
for bridge in self.bridges.keys():
|
|
||||||
self.ifup(bridge, iftype='bridge')
|
|
||||||
|
|
||||||
for interface in self.interfaces.keys():
|
|
||||||
self.ifup(interface)
|
|
||||||
else:
|
|
||||||
logger.info('No interface changes are required.')
|
|
||||||
|
|
||||||
return {_network_config_path(self.root_dir): new_config}
|
|
@ -1,835 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014-2015 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import logging
|
|
||||||
import re
|
|
||||||
|
|
||||||
import os_net_config
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def ifcfg_config_path(name):
|
|
||||||
return "/etc/sysconfig/network-scripts/ifcfg-%s" % name
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE(dprince): added here for testability
|
|
||||||
def bridge_config_path(name):
|
|
||||||
return ifcfg_config_path(name)
|
|
||||||
|
|
||||||
|
|
||||||
def ivs_config_path():
|
|
||||||
return "/etc/sysconfig/ivs"
|
|
||||||
|
|
||||||
|
|
||||||
def nfvswitch_config_path():
|
|
||||||
return "/etc/sysconfig/nfvswitch"
|
|
||||||
|
|
||||||
|
|
||||||
def route_config_path(name):
|
|
||||||
return "/etc/sysconfig/network-scripts/route-%s" % name
|
|
||||||
|
|
||||||
|
|
||||||
def route6_config_path(name):
|
|
||||||
return "/etc/sysconfig/network-scripts/route6-%s" % name
|
|
||||||
|
|
||||||
|
|
||||||
def cleanup_pattern():
|
|
||||||
return "/etc/sysconfig/network-scripts/ifcfg-*"
|
|
||||||
|
|
||||||
|
|
||||||
class IfcfgNetConfig(os_net_config.NetConfig):
|
|
||||||
"""Configure network interfaces using the ifcfg format."""
|
|
||||||
|
|
||||||
def __init__(self, noop=False, root_dir=''):
|
|
||||||
super(IfcfgNetConfig, self).__init__(noop, root_dir)
|
|
||||||
self.interface_data = {}
|
|
||||||
self.ivsinterface_data = {}
|
|
||||||
self.nfvswitch_intiface_data = {}
|
|
||||||
self.nfvswitch_cpus = None
|
|
||||||
self.vlan_data = {}
|
|
||||||
self.route_data = {}
|
|
||||||
self.route6_data = {}
|
|
||||||
self.bridge_data = {}
|
|
||||||
self.linuxbridge_data = {}
|
|
||||||
self.linuxbond_data = {}
|
|
||||||
self.ib_interface_data = {}
|
|
||||||
self.linuxteam_data = {}
|
|
||||||
self.member_names = {}
|
|
||||||
self.renamed_interfaces = {}
|
|
||||||
self.bond_primary_ifaces = {}
|
|
||||||
logger.info('Ifcfg net config provider created.')
|
|
||||||
|
|
||||||
def child_members(self, name):
|
|
||||||
children = set()
|
|
||||||
try:
|
|
||||||
for member in self.member_names[name]:
|
|
||||||
children.add(member)
|
|
||||||
children.update(self.child_members(member))
|
|
||||||
except KeyError:
|
|
||||||
pass
|
|
||||||
return children
|
|
||||||
|
|
||||||
def _add_common(self, base_opt):
|
|
||||||
|
|
||||||
ovs_extra = []
|
|
||||||
|
|
||||||
data = "# This file is autogenerated by os-net-config\n"
|
|
||||||
data += "DEVICE=%s\n" % base_opt.name
|
|
||||||
data += "ONBOOT=yes\n"
|
|
||||||
data += "HOTPLUG=no\n"
|
|
||||||
data += "NM_CONTROLLED=no\n"
|
|
||||||
if not base_opt.dns_servers and not base_opt.use_dhcp:
|
|
||||||
data += "PEERDNS=no\n"
|
|
||||||
if isinstance(base_opt, objects.Vlan):
|
|
||||||
if not base_opt.ovs_port:
|
|
||||||
# vlans on OVS bridges are internal ports (no device, etc)
|
|
||||||
data += "VLAN=yes\n"
|
|
||||||
if base_opt.device:
|
|
||||||
data += "PHYSDEV=%s\n" % base_opt.device
|
|
||||||
else:
|
|
||||||
if base_opt.linux_bond_name:
|
|
||||||
data += "PHYSDEV=%s\n" % base_opt.linux_bond_name
|
|
||||||
elif isinstance(base_opt, objects.IvsInterface):
|
|
||||||
data += "TYPE=IVSIntPort\n"
|
|
||||||
elif isinstance(base_opt, objects.NfvswitchInternal):
|
|
||||||
data += "TYPE=NFVSWITCHIntPort\n"
|
|
||||||
elif isinstance(base_opt, objects.IbInterface):
|
|
||||||
data += "TYPE=Infiniband\n"
|
|
||||||
elif re.match('\w+\.\d+$', base_opt.name):
|
|
||||||
data += "VLAN=yes\n"
|
|
||||||
if base_opt.linux_bond_name:
|
|
||||||
data += "MASTER=%s\n" % base_opt.linux_bond_name
|
|
||||||
data += "SLAVE=yes\n"
|
|
||||||
if base_opt.linux_team_name:
|
|
||||||
data += "TEAM_MASTER=%s\n" % base_opt.linux_team_name
|
|
||||||
if base_opt.primary:
|
|
||||||
data += "TEAM_PORT_CONFIG='{\"prio\": 100}'\n"
|
|
||||||
if base_opt.ivs_bridge_name:
|
|
||||||
data += "DEVICETYPE=ivs\n"
|
|
||||||
data += "IVS_BRIDGE=%s\n" % base_opt.ivs_bridge_name
|
|
||||||
if base_opt.nfvswitch_bridge_name:
|
|
||||||
data += "DEVICETYPE=nfvswitch\n"
|
|
||||||
data += "NFVSWITCH_BRIDGE=%s\n" % base_opt.nfvswitch_bridge_name
|
|
||||||
if base_opt.ovs_port:
|
|
||||||
if not isinstance(base_opt, objects.LinuxTeam):
|
|
||||||
data += "DEVICETYPE=ovs\n"
|
|
||||||
if base_opt.bridge_name:
|
|
||||||
if isinstance(base_opt, objects.Vlan):
|
|
||||||
data += "TYPE=OVSIntPort\n"
|
|
||||||
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
||||||
data += "OVS_OPTIONS=\"tag=%s\"\n" % base_opt.vlan_id
|
|
||||||
else:
|
|
||||||
data += "TYPE=OVSPort\n"
|
|
||||||
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
||||||
if base_opt.linux_bridge_name:
|
|
||||||
data += "BRIDGE=%s\n" % base_opt.linux_bridge_name
|
|
||||||
if isinstance(base_opt, objects.OvsBridge):
|
|
||||||
data += "DEVICETYPE=ovs\n"
|
|
||||||
data += "TYPE=OVSBridge\n"
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += "OVSBOOTPROTO=dhcp\n"
|
|
||||||
if base_opt.members:
|
|
||||||
members = [member.name for member in base_opt.members]
|
|
||||||
self.member_names[base_opt.name] = members
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += ("OVSDHCPINTERFACES=\"%s\"\n" % " ".join(members))
|
|
||||||
if base_opt.primary_interface_name:
|
|
||||||
mac = utils.interface_mac(base_opt.primary_interface_name)
|
|
||||||
ovs_extra.append("set bridge %s other-config:hwaddr=%s" %
|
|
||||||
(base_opt.name, mac))
|
|
||||||
if base_opt.ovs_options:
|
|
||||||
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
||||||
ovs_extra.extend(base_opt.ovs_extra)
|
|
||||||
elif isinstance(base_opt, objects.OvsBond):
|
|
||||||
if base_opt.primary_interface_name:
|
|
||||||
primary_name = base_opt.primary_interface_name
|
|
||||||
self.bond_primary_ifaces[base_opt.name] = primary_name
|
|
||||||
data += "DEVICETYPE=ovs\n"
|
|
||||||
data += "TYPE=OVSBond\n"
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += "OVSBOOTPROTO=dhcp\n"
|
|
||||||
if base_opt.members:
|
|
||||||
members = [member.name for member in base_opt.members]
|
|
||||||
self.member_names[base_opt.name] = members
|
|
||||||
data += ("BOND_IFACES=\"%s\"\n" % " ".join(members))
|
|
||||||
if base_opt.ovs_options:
|
|
||||||
data += "OVS_OPTIONS=\"%s\"\n" % base_opt.ovs_options
|
|
||||||
ovs_extra.extend(base_opt.ovs_extra)
|
|
||||||
elif isinstance(base_opt, objects.LinuxBridge):
|
|
||||||
data += "TYPE=Bridge\n"
|
|
||||||
data += "DELAY=0\n"
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += "BOOTPROTO=dhcp\n"
|
|
||||||
if base_opt.members:
|
|
||||||
members = [member.name for member in base_opt.members]
|
|
||||||
self.member_names[base_opt.name] = members
|
|
||||||
if base_opt.primary_interface_name:
|
|
||||||
primary_name = base_opt.primary_interface_name
|
|
||||||
primary_mac = utils.interface_mac(primary_name)
|
|
||||||
data += "MACADDR=\"%s\"\n" % primary_mac
|
|
||||||
elif isinstance(base_opt, objects.LinuxBond):
|
|
||||||
if base_opt.primary_interface_name:
|
|
||||||
primary_name = base_opt.primary_interface_name
|
|
||||||
primary_mac = utils.interface_mac(primary_name)
|
|
||||||
data += "MACADDR=\"%s\"\n" % primary_mac
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += "BOOTPROTO=dhcp\n"
|
|
||||||
if base_opt.members:
|
|
||||||
members = [member.name for member in base_opt.members]
|
|
||||||
self.member_names[base_opt.name] = members
|
|
||||||
if base_opt.bonding_options:
|
|
||||||
data += "BONDING_OPTS=\"%s\"\n" % base_opt.bonding_options
|
|
||||||
elif isinstance(base_opt, objects.LinuxTeam):
|
|
||||||
if base_opt.primary_interface_name:
|
|
||||||
primary_name = base_opt.primary_interface_name
|
|
||||||
primary_mac = utils.interface_mac(primary_name)
|
|
||||||
data += "MACADDR=\"%s\"\n" % primary_mac
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += "BOOTPROTO=dhcp\n"
|
|
||||||
if base_opt.members:
|
|
||||||
members = [member.name for member in base_opt.members]
|
|
||||||
self.member_names[base_opt.name] = members
|
|
||||||
data += "DEVICETYPE=Team\n"
|
|
||||||
if base_opt.bonding_options:
|
|
||||||
data += "TEAM_CONFIG='%s'\n" % base_opt.bonding_options
|
|
||||||
elif isinstance(base_opt, objects.OvsTunnel):
|
|
||||||
ovs_extra.extend(base_opt.ovs_extra)
|
|
||||||
data += "DEVICETYPE=ovs\n"
|
|
||||||
data += "TYPE=OVSTunnel\n"
|
|
||||||
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
||||||
data += "OVS_TUNNEL_TYPE=%s\n" % base_opt.tunnel_type
|
|
||||||
data += "OVS_TUNNEL_OPTIONS=\"%s\"\n" % \
|
|
||||||
' '.join(base_opt.ovs_options)
|
|
||||||
elif isinstance(base_opt, objects.OvsPatchPort):
|
|
||||||
ovs_extra.extend(base_opt.ovs_extra)
|
|
||||||
data += "DEVICETYPE=ovs\n"
|
|
||||||
data += "TYPE=OVSPatchPort\n"
|
|
||||||
data += "OVS_BRIDGE=%s\n" % base_opt.bridge_name
|
|
||||||
data += "OVS_PATCH_PEER=%s\n" % base_opt.peer
|
|
||||||
else:
|
|
||||||
if base_opt.use_dhcp:
|
|
||||||
data += "BOOTPROTO=dhcp\n"
|
|
||||||
elif not base_opt.addresses:
|
|
||||||
data += "BOOTPROTO=none\n"
|
|
||||||
|
|
||||||
if base_opt.mtu:
|
|
||||||
data += "MTU=%i\n" % base_opt.mtu
|
|
||||||
if base_opt.use_dhcpv6 or base_opt.v6_addresses():
|
|
||||||
data += "IPV6INIT=yes\n"
|
|
||||||
if base_opt.mtu:
|
|
||||||
data += "IPV6_MTU=%i\n" % base_opt.mtu
|
|
||||||
if base_opt.use_dhcpv6:
|
|
||||||
data += "DHCPV6C=yes\n"
|
|
||||||
elif base_opt.addresses:
|
|
||||||
v4_addresses = base_opt.v4_addresses()
|
|
||||||
if v4_addresses:
|
|
||||||
data += "BOOTPROTO=static\n"
|
|
||||||
for i, address in enumerate(v4_addresses):
|
|
||||||
num = '%s' % i if i else ''
|
|
||||||
data += "IPADDR%s=%s\n" % (num, address.ip)
|
|
||||||
data += "NETMASK%s=%s\n" % (num, address.netmask)
|
|
||||||
|
|
||||||
v6_addresses = base_opt.v6_addresses()
|
|
||||||
if v6_addresses:
|
|
||||||
first_v6 = v6_addresses[0]
|
|
||||||
data += "IPV6_AUTOCONF=no\n"
|
|
||||||
data += "IPV6ADDR=%s\n" % first_v6.ip_netmask
|
|
||||||
if len(v6_addresses) > 1:
|
|
||||||
secondaries_v6 = " ".join(map(lambda a: a.ip_netmask,
|
|
||||||
v6_addresses[1:]))
|
|
||||||
data += "IPV6ADDR_SECONDARIES=\"%s\"\n" % secondaries_v6
|
|
||||||
|
|
||||||
if base_opt.hwaddr:
|
|
||||||
data += "HWADDR=%s\n" % base_opt.hwaddr
|
|
||||||
if ovs_extra:
|
|
||||||
data += "OVS_EXTRA=\"%s\"\n" % " -- ".join(ovs_extra)
|
|
||||||
if not base_opt.defroute:
|
|
||||||
data += "DEFROUTE=no\n"
|
|
||||||
if base_opt.dhclient_args:
|
|
||||||
data += "DHCLIENTARGS=%s\n" % base_opt.dhclient_args
|
|
||||||
if base_opt.dns_servers:
|
|
||||||
data += "DNS1=%s\n" % base_opt.dns_servers[0]
|
|
||||||
if len(base_opt.dns_servers) == 2:
|
|
||||||
data += "DNS2=%s\n" % base_opt.dns_servers[1]
|
|
||||||
elif len(base_opt.dns_servers) > 2:
|
|
||||||
logger.warning('ifcfg format supports a max of 2 dns servers.')
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _add_routes(self, interface_name, routes=[]):
|
|
||||||
logger.info('adding custom route for interface: %s' % interface_name)
|
|
||||||
data = ""
|
|
||||||
first_line = ""
|
|
||||||
data6 = ""
|
|
||||||
first_line6 = ""
|
|
||||||
for route in routes:
|
|
||||||
if ":" not in route.next_hop:
|
|
||||||
# Route is an IPv4 route
|
|
||||||
if route.default:
|
|
||||||
first_line = "default via %s dev %s\n" % (route.next_hop,
|
|
||||||
interface_name)
|
|
||||||
else:
|
|
||||||
data += "%s via %s dev %s\n" % (route.ip_netmask,
|
|
||||||
route.next_hop,
|
|
||||||
interface_name)
|
|
||||||
else:
|
|
||||||
# Route is an IPv6 route
|
|
||||||
if route.default:
|
|
||||||
first_line6 = "default via %s dev %s\n" % (route.next_hop,
|
|
||||||
interface_name)
|
|
||||||
else:
|
|
||||||
data6 += "%s via %s dev %s\n" % (route.ip_netmask,
|
|
||||||
route.next_hop,
|
|
||||||
interface_name)
|
|
||||||
self.route_data[interface_name] = first_line + data
|
|
||||||
self.route6_data[interface_name] = first_line6 + data6
|
|
||||||
logger.debug('route data: %s' % self.route_data[interface_name])
|
|
||||||
logger.debug('ipv6 route data: %s' % self.route6_data[interface_name])
|
|
||||||
|
|
||||||
def add_interface(self, interface):
|
|
||||||
"""Add an Interface object to the net config object.
|
|
||||||
|
|
||||||
:param interface: The Interface object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding interface: %s' % interface.name)
|
|
||||||
data = self._add_common(interface)
|
|
||||||
logger.debug('interface data: %s' % data)
|
|
||||||
self.interface_data[interface.name] = data
|
|
||||||
if interface.routes:
|
|
||||||
self._add_routes(interface.name, interface.routes)
|
|
||||||
|
|
||||||
if interface.renamed:
|
|
||||||
logger.info("Interface %s being renamed to %s"
|
|
||||||
% (interface.hwname, interface.name))
|
|
||||||
self.renamed_interfaces[interface.hwname] = interface.name
|
|
||||||
|
|
||||||
def add_vlan(self, vlan):
|
|
||||||
"""Add a Vlan object to the net config object.
|
|
||||||
|
|
||||||
:param vlan: The vlan object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding vlan: %s' % vlan.name)
|
|
||||||
data = self._add_common(vlan)
|
|
||||||
logger.debug('vlan data: %s' % data)
|
|
||||||
self.vlan_data[vlan.name] = data
|
|
||||||
if vlan.routes:
|
|
||||||
self._add_routes(vlan.name, vlan.routes)
|
|
||||||
|
|
||||||
def add_ivs_interface(self, ivs_interface):
|
|
||||||
"""Add a ivs_interface object to the net config object.
|
|
||||||
|
|
||||||
:param ivs_interface: The ivs_interface object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding ivs_interface: %s' % ivs_interface.name)
|
|
||||||
data = self._add_common(ivs_interface)
|
|
||||||
logger.debug('ivs_interface data: %s' % data)
|
|
||||||
self.ivsinterface_data[ivs_interface.name] = data
|
|
||||||
if ivs_interface.routes:
|
|
||||||
self._add_routes(ivs_interface.name, ivs_interface.routes)
|
|
||||||
|
|
||||||
def add_nfvswitch_internal(self, nfvswitch_internal):
|
|
||||||
"""Add a nfvswitch_internal interface object to the net config object.
|
|
||||||
|
|
||||||
:param nfvswitch_internal: The nfvswitch_internal object to add.
|
|
||||||
"""
|
|
||||||
iface_name = nfvswitch_internal.name
|
|
||||||
logger.info('adding nfvswitch_internal interface: %s' % iface_name)
|
|
||||||
data = self._add_common(nfvswitch_internal)
|
|
||||||
logger.debug('nfvswitch_internal interface data: %s' % data)
|
|
||||||
self.nfvswitch_intiface_data[iface_name] = data
|
|
||||||
if nfvswitch_internal.routes:
|
|
||||||
self._add_routes(iface_name, nfvswitch_internal.routes)
|
|
||||||
|
|
||||||
def add_bridge(self, bridge):
|
|
||||||
"""Add an OvsBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The OvsBridge object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding bridge: %s' % bridge.name)
|
|
||||||
data = self._add_common(bridge)
|
|
||||||
logger.debug('bridge data: %s' % data)
|
|
||||||
self.bridge_data[bridge.name] = data
|
|
||||||
if bridge.routes:
|
|
||||||
self._add_routes(bridge.name, bridge.routes)
|
|
||||||
|
|
||||||
def add_linux_bridge(self, bridge):
|
|
||||||
"""Add a LinuxBridge object to the net config object.
|
|
||||||
|
|
||||||
:param bridge: The LinuxBridge object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding linux bridge: %s' % bridge.name)
|
|
||||||
data = self._add_common(bridge)
|
|
||||||
logger.debug('bridge data: %s' % data)
|
|
||||||
self.linuxbridge_data[bridge.name] = data
|
|
||||||
if bridge.routes:
|
|
||||||
self._add_routes(bridge.name, bridge.routes)
|
|
||||||
|
|
||||||
def add_ivs_bridge(self, bridge):
|
|
||||||
"""Add a IvsBridge object to the net config object.
|
|
||||||
|
|
||||||
IVS can only support one virtual switch per node,
|
|
||||||
using "ivs" as its name. As long as the ivs service
|
|
||||||
is running, the ivs virtual switch will be there.
|
|
||||||
It is impossible to add multiple ivs virtual switches
|
|
||||||
per node.
|
|
||||||
:param bridge: The IvsBridge object to add.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def add_nfvswitch_bridge(self, bridge):
|
|
||||||
"""Add a NFVSwitchBridge object to the net config object.
|
|
||||||
|
|
||||||
NFVSwitch can only support one virtual switch per node,
|
|
||||||
using "nfvswitch" as its name. As long as the nfvswitch service
|
|
||||||
is running, the nfvswitch virtual switch will be available.
|
|
||||||
:param bridge: The NfvswitchBridge object to add.
|
|
||||||
"""
|
|
||||||
self.nfvswitch_cpus = bridge.cpus
|
|
||||||
|
|
||||||
def add_bond(self, bond):
|
|
||||||
"""Add an OvsBond object to the net config object.
|
|
||||||
|
|
||||||
:param bond: The OvsBond object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding bond: %s' % bond.name)
|
|
||||||
data = self._add_common(bond)
|
|
||||||
logger.debug('bond data: %s' % data)
|
|
||||||
self.interface_data[bond.name] = data
|
|
||||||
if bond.routes:
|
|
||||||
self._add_routes(bond.name, bond.routes)
|
|
||||||
|
|
||||||
def add_linux_bond(self, bond):
|
|
||||||
"""Add a LinuxBond object to the net config object.
|
|
||||||
|
|
||||||
:param bond: The LinuxBond object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding linux bond: %s' % bond.name)
|
|
||||||
data = self._add_common(bond)
|
|
||||||
logger.debug('bond data: %s' % data)
|
|
||||||
self.linuxbond_data[bond.name] = data
|
|
||||||
if bond.routes:
|
|
||||||
self._add_routes(bond.name, bond.routes)
|
|
||||||
|
|
||||||
def add_linux_team(self, team):
|
|
||||||
"""Add a LinuxTeam object to the net config object.
|
|
||||||
|
|
||||||
:param team: The LinuxTeam object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding linux team: %s' % team.name)
|
|
||||||
data = self._add_common(team)
|
|
||||||
logger.debug('team data: %s' % data)
|
|
||||||
self.linuxteam_data[team.name] = data
|
|
||||||
if team.routes:
|
|
||||||
self._add_routes(team.name, team.routes)
|
|
||||||
|
|
||||||
def add_ovs_tunnel(self, tunnel):
|
|
||||||
"""Add a OvsTunnel object to the net config object.
|
|
||||||
|
|
||||||
:param tunnel: The OvsTunnel object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding ovs tunnel: %s' % tunnel.name)
|
|
||||||
data = self._add_common(tunnel)
|
|
||||||
logger.debug('ovs tunnel data: %s' % data)
|
|
||||||
self.interface_data[tunnel.name] = data
|
|
||||||
|
|
||||||
def add_ovs_patch_port(self, ovs_patch_port):
|
|
||||||
"""Add a OvsPatchPort object to the net config object.
|
|
||||||
|
|
||||||
:param ovs_patch_port: The OvsPatchPort object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding ovs patch port: %s' % ovs_patch_port.name)
|
|
||||||
data = self._add_common(ovs_patch_port)
|
|
||||||
logger.debug('ovs patch port data: %s' % data)
|
|
||||||
self.interface_data[ovs_patch_port.name] = data
|
|
||||||
|
|
||||||
def add_ib_interface(self, ib_interface):
|
|
||||||
"""Add an InfiniBand interface object to the net config object.
|
|
||||||
|
|
||||||
:param ib_interface: The InfiniBand interface object to add.
|
|
||||||
"""
|
|
||||||
logger.info('adding ib_interface: %s' % ib_interface.name)
|
|
||||||
data = self._add_common(ib_interface)
|
|
||||||
logger.debug('ib_interface data: %s' % data)
|
|
||||||
self.ib_interface_data[ib_interface.name] = data
|
|
||||||
if ib_interface.routes:
|
|
||||||
self._add_routes(ib_interface.name, ib_interface.routes)
|
|
||||||
|
|
||||||
if ib_interface.renamed:
|
|
||||||
logger.info("InfiniBand interface %s being renamed to %s"
|
|
||||||
% (ib_interface.hwname, ib_interface.name))
|
|
||||||
self.renamed_interfaces[ib_interface.hwname] = ib_interface.name
|
|
||||||
|
|
||||||
def generate_ivs_config(self, ivs_uplinks, ivs_interfaces):
|
|
||||||
"""Generate configuration content for ivs."""
|
|
||||||
|
|
||||||
intfs = []
|
|
||||||
for intf in ivs_uplinks:
|
|
||||||
intfs.append(' -u ')
|
|
||||||
intfs.append(intf)
|
|
||||||
uplink_str = ''.join(intfs)
|
|
||||||
|
|
||||||
intfs = []
|
|
||||||
for intf in ivs_interfaces:
|
|
||||||
intfs.append(' --internal-port=')
|
|
||||||
intfs.append(intf)
|
|
||||||
intf_str = ''.join(intfs)
|
|
||||||
|
|
||||||
data = ("DAEMON_ARGS=\"--hitless --certificate /etc/ivs "
|
|
||||||
"--inband-vlan 4092%s%s\""
|
|
||||||
% (uplink_str, intf_str))
|
|
||||||
return data
|
|
||||||
|
|
||||||
def generate_nfvswitch_config(self, nfvswitch_ifaces,
|
|
||||||
nfvswitch_internal_ifaces):
|
|
||||||
"""Generate configuration content for nfvswitch."""
|
|
||||||
|
|
||||||
cpu_str = ""
|
|
||||||
if self.nfvswitch_cpus:
|
|
||||||
cpu_str = " -c " + self.nfvswitch_cpus
|
|
||||||
|
|
||||||
ifaces = []
|
|
||||||
for iface in nfvswitch_ifaces:
|
|
||||||
ifaces.append(' -u ')
|
|
||||||
ifaces.append(iface)
|
|
||||||
iface_str = ''.join(ifaces)
|
|
||||||
|
|
||||||
ifaces = []
|
|
||||||
for iface in nfvswitch_internal_ifaces:
|
|
||||||
ifaces.append(' -m ')
|
|
||||||
ifaces.append(iface)
|
|
||||||
internal_str = ''.join(ifaces)
|
|
||||||
|
|
||||||
data = ("SETUP_ARGS=\"%s%s%s\"" % (cpu_str, iface_str, internal_str))
|
|
||||||
return data
|
|
||||||
|
|
||||||
def apply(self, cleanup=False, activate=True):
|
|
||||||
"""Apply the network configuration.
|
|
||||||
|
|
||||||
:param cleanup: A boolean which indicates whether any undefined
|
|
||||||
(existing but not present in the object model) interface
|
|
||||||
should be disabled and deleted.
|
|
||||||
:param activate: A boolean which indicates if the config should
|
|
||||||
be activated by stopping/starting interfaces
|
|
||||||
NOTE: if cleanup is specified we will deactivate interfaces even
|
|
||||||
if activate is false
|
|
||||||
:returns: a dict of the format: filename/data which contains info
|
|
||||||
for each file that was changed (or would be changed if in --noop
|
|
||||||
mode).
|
|
||||||
Note the noop mode is set via the constructor noop boolean
|
|
||||||
"""
|
|
||||||
logger.info('applying network configs...')
|
|
||||||
restart_interfaces = []
|
|
||||||
restart_vlans = []
|
|
||||||
restart_bridges = []
|
|
||||||
restart_linux_bonds = []
|
|
||||||
restart_linux_teams = []
|
|
||||||
update_files = {}
|
|
||||||
all_file_names = []
|
|
||||||
ivs_uplinks = [] # ivs physical uplinks
|
|
||||||
ivs_interfaces = [] # ivs internal ports
|
|
||||||
nfvswitch_interfaces = [] # nfvswitch physical interfaces
|
|
||||||
nfvswitch_internal_ifaces = [] # nfvswitch internal/management ports
|
|
||||||
|
|
||||||
for interface_name, iface_data in self.interface_data.iteritems():
|
|
||||||
route_data = self.route_data.get(interface_name, '')
|
|
||||||
route6_data = self.route6_data.get(interface_name, '')
|
|
||||||
interface_path = self.root_dir + ifcfg_config_path(interface_name)
|
|
||||||
route_path = self.root_dir + route_config_path(interface_name)
|
|
||||||
route6_path = self.root_dir + route6_config_path(interface_name)
|
|
||||||
all_file_names.append(interface_path)
|
|
||||||
all_file_names.append(route_path)
|
|
||||||
all_file_names.append(route6_path)
|
|
||||||
if "IVS_BRIDGE" in iface_data:
|
|
||||||
ivs_uplinks.append(interface_name)
|
|
||||||
if "NFVSWITCH_BRIDGE" in iface_data:
|
|
||||||
nfvswitch_interfaces.append(interface_name)
|
|
||||||
all_file_names.append(route6_path)
|
|
||||||
if (utils.diff(interface_path, iface_data) or
|
|
||||||
utils.diff(route_path, route_data) or
|
|
||||||
utils.diff(route6_path, route6_data)):
|
|
||||||
restart_interfaces.append(interface_name)
|
|
||||||
restart_interfaces.extend(self.child_members(interface_name))
|
|
||||||
update_files[interface_path] = iface_data
|
|
||||||
update_files[route_path] = route_data
|
|
||||||
update_files[route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for interface: %s' %
|
|
||||||
interface_name)
|
|
||||||
|
|
||||||
for interface_name, iface_data in self.ivsinterface_data.iteritems():
|
|
||||||
route_data = self.route_data.get(interface_name, '')
|
|
||||||
route6_data = self.route6_data.get(interface_name, '')
|
|
||||||
interface_path = self.root_dir + ifcfg_config_path(interface_name)
|
|
||||||
route_path = self.root_dir + route_config_path(interface_name)
|
|
||||||
route6_path = self.root_dir + route6_config_path(interface_name)
|
|
||||||
all_file_names.append(interface_path)
|
|
||||||
all_file_names.append(route_path)
|
|
||||||
all_file_names.append(route6_path)
|
|
||||||
ivs_interfaces.append(interface_name)
|
|
||||||
if (utils.diff(interface_path, iface_data) or
|
|
||||||
utils.diff(route_path, route_data)):
|
|
||||||
restart_interfaces.append(interface_name)
|
|
||||||
restart_interfaces.extend(self.child_members(interface_name))
|
|
||||||
update_files[interface_path] = iface_data
|
|
||||||
update_files[route_path] = route_data
|
|
||||||
update_files[route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for ivs interface: %s' %
|
|
||||||
interface_name)
|
|
||||||
|
|
||||||
for iface_name, iface_data in self.nfvswitch_intiface_data.iteritems():
|
|
||||||
route_data = self.route_data.get(iface_name, '')
|
|
||||||
route6_data = self.route6_data.get(iface_name, '')
|
|
||||||
iface_path = self.root_dir + ifcfg_config_path(iface_name)
|
|
||||||
route_path = self.root_dir + route_config_path(iface_name)
|
|
||||||
route6_path = self.root_dir + route6_config_path(iface_name)
|
|
||||||
all_file_names.append(iface_path)
|
|
||||||
all_file_names.append(route_path)
|
|
||||||
all_file_names.append(route6_path)
|
|
||||||
nfvswitch_internal_ifaces.append(iface_name)
|
|
||||||
if (utils.diff(iface_path, iface_data) or
|
|
||||||
utils.diff(route_path, route_data)):
|
|
||||||
restart_interfaces.append(iface_name)
|
|
||||||
restart_interfaces.extend(self.child_members(iface_name))
|
|
||||||
update_files[iface_path] = iface_data
|
|
||||||
update_files[route_path] = route_data
|
|
||||||
update_files[route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for nfvswitch interface: %s' %
|
|
||||||
iface_name)
|
|
||||||
|
|
||||||
for vlan_name, vlan_data in self.vlan_data.iteritems():
|
|
||||||
route_data = self.route_data.get(vlan_name, '')
|
|
||||||
route6_data = self.route6_data.get(vlan_name, '')
|
|
||||||
vlan_path = self.root_dir + ifcfg_config_path(vlan_name)
|
|
||||||
vlan_route_path = self.root_dir + route_config_path(vlan_name)
|
|
||||||
vlan_route6_path = self.root_dir + route6_config_path(vlan_name)
|
|
||||||
all_file_names.append(vlan_path)
|
|
||||||
all_file_names.append(vlan_route_path)
|
|
||||||
all_file_names.append(vlan_route6_path)
|
|
||||||
if (utils.diff(vlan_path, vlan_data) or
|
|
||||||
utils.diff(vlan_route_path, route_data)):
|
|
||||||
restart_vlans.append(vlan_name)
|
|
||||||
restart_vlans.extend(self.child_members(vlan_name))
|
|
||||||
update_files[vlan_path] = vlan_data
|
|
||||||
update_files[vlan_route_path] = route_data
|
|
||||||
update_files[vlan_route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for vlan interface: %s' %
|
|
||||||
vlan_name)
|
|
||||||
|
|
||||||
for bridge_name, bridge_data in self.bridge_data.iteritems():
|
|
||||||
route_data = self.route_data.get(bridge_name, '')
|
|
||||||
route6_data = self.route6_data.get(bridge_name, '')
|
|
||||||
bridge_path = self.root_dir + bridge_config_path(bridge_name)
|
|
||||||
br_route_path = self.root_dir + route_config_path(bridge_name)
|
|
||||||
br_route6_path = self.root_dir + route6_config_path(bridge_name)
|
|
||||||
all_file_names.append(bridge_path)
|
|
||||||
all_file_names.append(br_route_path)
|
|
||||||
all_file_names.append(br_route6_path)
|
|
||||||
if (utils.diff(bridge_path, bridge_data) or
|
|
||||||
utils.diff(br_route_path, route_data) or
|
|
||||||
utils.diff(br_route6_path, route6_data)):
|
|
||||||
restart_bridges.append(bridge_name)
|
|
||||||
restart_interfaces.extend(self.child_members(bridge_name))
|
|
||||||
update_files[bridge_path] = bridge_data
|
|
||||||
update_files[br_route_path] = route_data
|
|
||||||
update_files[br_route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for bridge: %s' % bridge_name)
|
|
||||||
|
|
||||||
for bridge_name, bridge_data in self.linuxbridge_data.iteritems():
|
|
||||||
route_data = self.route_data.get(bridge_name, '')
|
|
||||||
route6_data = self.route6_data.get(bridge_name, '')
|
|
||||||
bridge_path = self.root_dir + bridge_config_path(bridge_name)
|
|
||||||
br_route_path = self.root_dir + route_config_path(bridge_name)
|
|
||||||
br_route6_path = self.root_dir + route6_config_path(bridge_name)
|
|
||||||
all_file_names.append(bridge_path)
|
|
||||||
all_file_names.append(br_route_path)
|
|
||||||
all_file_names.append(br_route6_path)
|
|
||||||
if (utils.diff(bridge_path, bridge_data) or
|
|
||||||
utils.diff(br_route_path, route_data) or
|
|
||||||
utils.diff(br_route6_path, route6_data)):
|
|
||||||
restart_bridges.append(bridge_name)
|
|
||||||
restart_interfaces.extend(self.child_members(bridge_name))
|
|
||||||
update_files[bridge_path] = bridge_data
|
|
||||||
update_files[br_route_path] = route_data
|
|
||||||
update_files[br_route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for bridge: %s' % bridge_name)
|
|
||||||
|
|
||||||
for team_name, team_data in self.linuxteam_data.iteritems():
|
|
||||||
route_data = self.route_data.get(team_name, '')
|
|
||||||
route6_data = self.route6_data.get(team_name, '')
|
|
||||||
team_path = self.root_dir + bridge_config_path(team_name)
|
|
||||||
team_route_path = self.root_dir + route_config_path(team_name)
|
|
||||||
team_route6_path = self.root_dir + route6_config_path(team_name)
|
|
||||||
all_file_names.append(team_path)
|
|
||||||
all_file_names.append(team_route_path)
|
|
||||||
all_file_names.append(team_route6_path)
|
|
||||||
if (utils.diff(team_path, team_data) or
|
|
||||||
utils.diff(team_route_path, route_data) or
|
|
||||||
utils.diff(team_route6_path, route6_data)):
|
|
||||||
restart_linux_teams.append(team_name)
|
|
||||||
restart_interfaces.extend(self.child_members(team_name))
|
|
||||||
update_files[team_path] = team_data
|
|
||||||
update_files[team_route_path] = route_data
|
|
||||||
update_files[team_route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for linux team: %s' %
|
|
||||||
team_name)
|
|
||||||
|
|
||||||
for bond_name, bond_data in self.linuxbond_data.iteritems():
|
|
||||||
route_data = self.route_data.get(bond_name, '')
|
|
||||||
route6_data = self.route6_data.get(bond_name, '')
|
|
||||||
bond_path = self.root_dir + bridge_config_path(bond_name)
|
|
||||||
bond_route_path = self.root_dir + route_config_path(bond_name)
|
|
||||||
bond_route6_path = self.root_dir + route6_config_path(bond_name)
|
|
||||||
all_file_names.append(bond_path)
|
|
||||||
all_file_names.append(bond_route_path)
|
|
||||||
all_file_names.append(bond_route6_path)
|
|
||||||
if (utils.diff(bond_path, bond_data) or
|
|
||||||
utils.diff(bond_route_path, route_data) or
|
|
||||||
utils.diff(bond_route6_path, route6_data)):
|
|
||||||
restart_linux_bonds.append(bond_name)
|
|
||||||
restart_interfaces.extend(self.child_members(bond_name))
|
|
||||||
update_files[bond_path] = bond_data
|
|
||||||
update_files[bond_route_path] = route_data
|
|
||||||
update_files[bond_route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for linux bond: %s' %
|
|
||||||
bond_name)
|
|
||||||
|
|
||||||
# Infiniband interfaces are handled similarly to Ethernet interfaces
|
|
||||||
for interface_name, iface_data in self.ib_interface_data.iteritems():
|
|
||||||
route_data = self.route_data.get(interface_name, '')
|
|
||||||
route6_data = self.route6_data.get(interface_name, '')
|
|
||||||
interface_path = self.root_dir + ifcfg_config_path(interface_name)
|
|
||||||
route_path = self.root_dir + route_config_path(interface_name)
|
|
||||||
route6_path = self.root_dir + route6_config_path(interface_name)
|
|
||||||
all_file_names.append(interface_path)
|
|
||||||
all_file_names.append(route_path)
|
|
||||||
all_file_names.append(route6_path)
|
|
||||||
# TODO(dsneddon) determine if InfiniBand can be used with IVS
|
|
||||||
if "IVS_BRIDGE" in iface_data:
|
|
||||||
ivs_uplinks.append(interface_name)
|
|
||||||
all_file_names.append(route6_path)
|
|
||||||
if (utils.diff(interface_path, iface_data) or
|
|
||||||
utils.diff(route_path, route_data) or
|
|
||||||
utils.diff(route6_path, route6_data)):
|
|
||||||
restart_interfaces.append(interface_name)
|
|
||||||
restart_interfaces.extend(self.child_members(interface_name))
|
|
||||||
update_files[interface_path] = iface_data
|
|
||||||
update_files[route_path] = route_data
|
|
||||||
update_files[route6_path] = route6_data
|
|
||||||
else:
|
|
||||||
logger.info('No changes required for InfiniBand iface: %s' %
|
|
||||||
interface_name)
|
|
||||||
|
|
||||||
if cleanup:
|
|
||||||
for ifcfg_file in glob.iglob(cleanup_pattern()):
|
|
||||||
if ifcfg_file not in all_file_names:
|
|
||||||
interface_name = ifcfg_file[len(cleanup_pattern()) - 1:]
|
|
||||||
if interface_name != 'lo':
|
|
||||||
logger.info('cleaning up interface: %s'
|
|
||||||
% interface_name)
|
|
||||||
self.ifdown(interface_name)
|
|
||||||
self.remove_config(ifcfg_file)
|
|
||||||
|
|
||||||
if activate:
|
|
||||||
for vlan in restart_vlans:
|
|
||||||
self.ifdown(vlan)
|
|
||||||
|
|
||||||
for interface in restart_interfaces:
|
|
||||||
self.ifdown(interface)
|
|
||||||
|
|
||||||
for linux_bond in restart_linux_bonds:
|
|
||||||
self.ifdown(linux_bond)
|
|
||||||
|
|
||||||
for linux_team in restart_linux_teams:
|
|
||||||
self.ifdown(linux_team)
|
|
||||||
|
|
||||||
for bridge in restart_bridges:
|
|
||||||
self.ifdown(bridge, iftype='bridge')
|
|
||||||
|
|
||||||
for oldname, newname in self.renamed_interfaces.iteritems():
|
|
||||||
self.ifrename(oldname, newname)
|
|
||||||
|
|
||||||
for location, data in update_files.iteritems():
|
|
||||||
self.write_config(location, data)
|
|
||||||
|
|
||||||
if ivs_uplinks or ivs_interfaces:
|
|
||||||
location = ivs_config_path()
|
|
||||||
data = self.generate_ivs_config(ivs_uplinks, ivs_interfaces)
|
|
||||||
self.write_config(location, data)
|
|
||||||
|
|
||||||
if nfvswitch_interfaces or nfvswitch_internal_ifaces:
|
|
||||||
location = nfvswitch_config_path()
|
|
||||||
data = self.generate_nfvswitch_config(nfvswitch_interfaces,
|
|
||||||
nfvswitch_internal_ifaces)
|
|
||||||
self.write_config(location, data)
|
|
||||||
|
|
||||||
if activate:
|
|
||||||
for linux_team in restart_linux_teams:
|
|
||||||
self.ifup(linux_team)
|
|
||||||
|
|
||||||
for bridge in restart_bridges:
|
|
||||||
self.ifup(bridge, iftype='bridge')
|
|
||||||
|
|
||||||
for interface in restart_interfaces:
|
|
||||||
self.ifup(interface)
|
|
||||||
|
|
||||||
for linux_bond in restart_linux_bonds:
|
|
||||||
self.ifup(linux_bond)
|
|
||||||
|
|
||||||
for bond in self.bond_primary_ifaces:
|
|
||||||
self.ovs_appctl('bond/set-active-slave', bond,
|
|
||||||
self.bond_primary_ifaces[bond])
|
|
||||||
|
|
||||||
if ivs_uplinks or ivs_interfaces:
|
|
||||||
logger.info("Attach to ivs with "
|
|
||||||
"uplinks: %s, "
|
|
||||||
"interfaces: %s" %
|
|
||||||
(ivs_uplinks, ivs_interfaces))
|
|
||||||
for ivs_uplink in ivs_uplinks:
|
|
||||||
self.ifup(ivs_uplink)
|
|
||||||
for ivs_interface in ivs_interfaces:
|
|
||||||
self.ifup(ivs_interface)
|
|
||||||
msg = "Restart ivs"
|
|
||||||
self.execute(msg, '/usr/bin/systemctl',
|
|
||||||
'restart', 'ivs')
|
|
||||||
|
|
||||||
if nfvswitch_interfaces or nfvswitch_internal_ifaces:
|
|
||||||
logger.info("Attach to nfvswitch with "
|
|
||||||
"interfaces: %s, "
|
|
||||||
"internal interfaces: %s" %
|
|
||||||
(nfvswitch_interfaces, nfvswitch_internal_ifaces))
|
|
||||||
for nfvswitch_interface in nfvswitch_interfaces:
|
|
||||||
self.ifup(nfvswitch_interface)
|
|
||||||
for nfvswitch_internal in nfvswitch_internal_ifaces:
|
|
||||||
self.ifup(nfvswitch_internal)
|
|
||||||
msg = "Restart nfvswitch"
|
|
||||||
self.execute(msg, '/usr/bin/systemctl',
|
|
||||||
'restart', 'nfvswitch')
|
|
||||||
|
|
||||||
for vlan in restart_vlans:
|
|
||||||
self.ifup(vlan)
|
|
||||||
|
|
||||||
return update_files
|
|
@ -1,21 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os_net_config
|
|
||||||
|
|
||||||
|
|
||||||
class IprouteNetConfig(os_net_config.NetConfig):
|
|
||||||
"""Configure network interfaces using iproute2."""
|
|
@ -1,875 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import netaddr
|
|
||||||
from oslo_utils import strutils
|
|
||||||
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
_NUMBERED_NICS = None
|
|
||||||
|
|
||||||
|
|
||||||
class InvalidConfigException(ValueError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def object_from_json(json):
|
|
||||||
obj_type = json.get("type")
|
|
||||||
if obj_type == "interface":
|
|
||||||
return Interface.from_json(json)
|
|
||||||
elif obj_type == "vlan":
|
|
||||||
return Vlan.from_json(json)
|
|
||||||
elif obj_type == "ovs_bridge":
|
|
||||||
return OvsBridge.from_json(json)
|
|
||||||
elif obj_type == "ovs_bond":
|
|
||||||
return OvsBond.from_json(json)
|
|
||||||
elif obj_type == "linux_bond":
|
|
||||||
return LinuxBond.from_json(json)
|
|
||||||
elif obj_type == "team":
|
|
||||||
return LinuxTeam.from_json(json)
|
|
||||||
elif obj_type == "linux_bridge":
|
|
||||||
return LinuxBridge.from_json(json)
|
|
||||||
elif obj_type == "ivs_bridge":
|
|
||||||
return IvsBridge.from_json(json)
|
|
||||||
elif obj_type == "ivs_interface":
|
|
||||||
return IvsInterface.from_json(json)
|
|
||||||
elif obj_type == "nfvswitch_bridge":
|
|
||||||
return NfvswitchBridge.from_json(json)
|
|
||||||
elif obj_type == "nfvswitch_internal":
|
|
||||||
return NfvswitchInternal.from_json(json)
|
|
||||||
elif obj_type == "ovs_tunnel":
|
|
||||||
return OvsTunnel.from_json(json)
|
|
||||||
elif obj_type == "ovs_patch_port":
|
|
||||||
return OvsPatchPort.from_json(json)
|
|
||||||
elif obj_type == "ib_interface":
|
|
||||||
return IbInterface.from_json(json)
|
|
||||||
|
|
||||||
|
|
||||||
def _get_required_field(json, name, object_name):
|
|
||||||
field = json.get(name)
|
|
||||||
if not field:
|
|
||||||
msg = '%s JSON objects require \'%s\' to be configured.' \
|
|
||||||
% (object_name, name)
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
return field
|
|
||||||
|
|
||||||
|
|
||||||
def _numbered_nics(nic_mapping=None):
|
|
||||||
mapping = nic_mapping or {}
|
|
||||||
global _NUMBERED_NICS
|
|
||||||
if _NUMBERED_NICS:
|
|
||||||
return _NUMBERED_NICS
|
|
||||||
_NUMBERED_NICS = {}
|
|
||||||
count = 0
|
|
||||||
active_nics = utils.ordered_active_nics()
|
|
||||||
for nic in active_nics:
|
|
||||||
count += 1
|
|
||||||
nic_alias = "nic%i" % count
|
|
||||||
nic_mapped = mapping.get(nic_alias, nic)
|
|
||||||
|
|
||||||
# The mapping is either invalid, or specifies a mac
|
|
||||||
if nic_mapped not in active_nics:
|
|
||||||
for active in active_nics:
|
|
||||||
try:
|
|
||||||
active_mac = utils.interface_mac(active)
|
|
||||||
except IOError:
|
|
||||||
continue
|
|
||||||
if nic_mapped == active_mac:
|
|
||||||
logger.debug("%s matches device %s" % (nic_mapped, active))
|
|
||||||
nic_mapped = active
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
# The mapping can't specify a non-active or non-existent nic
|
|
||||||
logger.warning('interface %s is not in an active nic (%s)'
|
|
||||||
% (nic_mapped, ', '.join(active_nics)))
|
|
||||||
continue
|
|
||||||
|
|
||||||
# Duplicate mappings are not allowed
|
|
||||||
if nic_mapped in _NUMBERED_NICS.values():
|
|
||||||
msg = ('interface %s already mapped, '
|
|
||||||
'check mapping file for duplicates'
|
|
||||||
% nic_mapped)
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
_NUMBERED_NICS[nic_alias] = nic_mapped
|
|
||||||
logger.info("%s mapped to: %s" % (nic_alias, nic_mapped))
|
|
||||||
if not _NUMBERED_NICS:
|
|
||||||
logger.warning('No active nics found.')
|
|
||||||
return _NUMBERED_NICS
|
|
||||||
|
|
||||||
|
|
||||||
class Route(object):
|
|
||||||
"""Base class for network routes."""
|
|
||||||
|
|
||||||
def __init__(self, next_hop, ip_netmask="", default=False):
|
|
||||||
self.next_hop = next_hop
|
|
||||||
self.ip_netmask = ip_netmask
|
|
||||||
self.default = default
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
next_hop = _get_required_field(json, 'next_hop', 'Route')
|
|
||||||
ip_netmask = json.get('ip_netmask', "")
|
|
||||||
default = strutils.bool_from_string(str(json.get('default', False)))
|
|
||||||
return Route(next_hop, ip_netmask, default)
|
|
||||||
|
|
||||||
|
|
||||||
class Address(object):
|
|
||||||
"""Base class for network addresses."""
|
|
||||||
|
|
||||||
def __init__(self, ip_netmask):
|
|
||||||
self.ip_netmask = ip_netmask
|
|
||||||
ip_nw = netaddr.IPNetwork(self.ip_netmask)
|
|
||||||
self.ip = str(ip_nw.ip)
|
|
||||||
self.netmask = str(ip_nw.netmask)
|
|
||||||
self.prefixlen = ip_nw.prefixlen
|
|
||||||
self.version = ip_nw.version
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
ip_netmask = _get_required_field(json, 'ip_netmask', 'Address')
|
|
||||||
return Address(ip_netmask)
|
|
||||||
|
|
||||||
|
|
||||||
class _BaseOpts(object):
|
|
||||||
"""Base abstraction for logical port options."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
numbered_nic_names = _numbered_nics(nic_mapping)
|
|
||||||
self.hwaddr = None
|
|
||||||
self.hwname = None
|
|
||||||
self.renamed = False
|
|
||||||
if name in numbered_nic_names:
|
|
||||||
if persist_mapping:
|
|
||||||
self.name = name
|
|
||||||
self.hwname = numbered_nic_names[name]
|
|
||||||
self.hwaddr = utils.interface_mac(self.hwname)
|
|
||||||
self.renamed = True
|
|
||||||
else:
|
|
||||||
self.name = numbered_nic_names[name]
|
|
||||||
else:
|
|
||||||
self.name = name
|
|
||||||
|
|
||||||
self.mtu = mtu
|
|
||||||
self.use_dhcp = use_dhcp
|
|
||||||
self.use_dhcpv6 = use_dhcpv6
|
|
||||||
self.addresses = addresses
|
|
||||||
self.routes = routes
|
|
||||||
self.primary = primary
|
|
||||||
self.defroute = defroute
|
|
||||||
self.dhclient_args = dhclient_args
|
|
||||||
self.dns_servers = dns_servers
|
|
||||||
self.bridge_name = None # internal
|
|
||||||
self.linux_bridge_name = None # internal
|
|
||||||
self.ivs_bridge_name = None # internal
|
|
||||||
self.nfvswitch_bridge_name = None # internal
|
|
||||||
self.linux_bond_name = None # internal
|
|
||||||
self.linux_team_name = None # internal
|
|
||||||
self.ovs_port = False # internal
|
|
||||||
self.primary_interface_name = None # internal
|
|
||||||
|
|
||||||
def v4_addresses(self):
|
|
||||||
v4_addresses = []
|
|
||||||
for addr in self.addresses:
|
|
||||||
if addr.version == 4:
|
|
||||||
v4_addresses.append(addr)
|
|
||||||
|
|
||||||
return v4_addresses
|
|
||||||
|
|
||||||
def v6_addresses(self):
|
|
||||||
v6_addresses = []
|
|
||||||
for addr in self.addresses:
|
|
||||||
if addr.version == 6:
|
|
||||||
v6_addresses.append(addr)
|
|
||||||
|
|
||||||
return v6_addresses
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def base_opts_from_json(json, include_primary=True):
|
|
||||||
use_dhcp = strutils.bool_from_string(str(json.get('use_dhcp', False)))
|
|
||||||
use_dhcpv6 = strutils.bool_from_string(str(json.get('use_dhcpv6',
|
|
||||||
False)))
|
|
||||||
defroute = strutils.bool_from_string(str(json.get('defroute',
|
|
||||||
True)))
|
|
||||||
mtu = json.get('mtu', None)
|
|
||||||
dhclient_args = json.get('dhclient_args')
|
|
||||||
dns_servers = json.get('dns_servers')
|
|
||||||
primary = strutils.bool_from_string(str(json.get('primary', False)))
|
|
||||||
addresses = []
|
|
||||||
routes = []
|
|
||||||
|
|
||||||
# addresses
|
|
||||||
addresses_json = json.get('addresses')
|
|
||||||
if addresses_json:
|
|
||||||
if isinstance(addresses_json, list):
|
|
||||||
for address in addresses_json:
|
|
||||||
addresses.append(Address.from_json(address))
|
|
||||||
else:
|
|
||||||
msg = 'Addresses must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
# routes
|
|
||||||
routes_json = json.get('routes')
|
|
||||||
if routes_json:
|
|
||||||
if isinstance(routes_json, list):
|
|
||||||
for route in routes_json:
|
|
||||||
routes.append(Route.from_json(route))
|
|
||||||
else:
|
|
||||||
msg = 'Routes must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
nic_mapping = json.get('nic_mapping')
|
|
||||||
persist_mapping = json.get('persist_mapping')
|
|
||||||
|
|
||||||
if include_primary:
|
|
||||||
return (use_dhcp, use_dhcpv6, addresses, routes, mtu, primary,
|
|
||||||
nic_mapping, persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
else:
|
|
||||||
return (use_dhcp, use_dhcpv6, addresses, routes, mtu,
|
|
||||||
nic_mapping, persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class Interface(_BaseOpts):
|
|
||||||
"""Base class for network interfaces."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(Interface, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'Interface')
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return Interface(name, *opts)
|
|
||||||
|
|
||||||
|
|
||||||
class Vlan(_BaseOpts):
|
|
||||||
"""Base class for VLANs.
|
|
||||||
|
|
||||||
NOTE: the name parameter must be formated w/ vlan<num> where <num>
|
|
||||||
matches the vlan ID being used. Example: vlan5
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, device, vlan_id, use_dhcp=False, use_dhcpv6=False,
|
|
||||||
addresses=None, routes=None, mtu=None, primary=False,
|
|
||||||
nic_mapping=None, persist_mapping=False, defroute=True,
|
|
||||||
dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
name = 'vlan%i' % vlan_id
|
|
||||||
super(Vlan, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
self.vlan_id = int(vlan_id)
|
|
||||||
|
|
||||||
numbered_nic_names = _numbered_nics(nic_mapping)
|
|
||||||
if device in numbered_nic_names:
|
|
||||||
self.device = numbered_nic_names[device]
|
|
||||||
else:
|
|
||||||
self.device = device
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
# A vlan on an OVS bridge won't require a device (OVS Int Port)
|
|
||||||
device = json.get('device')
|
|
||||||
vlan_id = _get_required_field(json, 'vlan_id', 'Vlan')
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return Vlan(device, vlan_id, *opts)
|
|
||||||
|
|
||||||
|
|
||||||
class IvsInterface(_BaseOpts):
|
|
||||||
"""Base class for ivs interfaces."""
|
|
||||||
|
|
||||||
def __init__(self, vlan_id, name='ivs', use_dhcp=False, use_dhcpv6=False,
|
|
||||||
addresses=None, routes=None, mtu=1500, primary=False,
|
|
||||||
nic_mapping=None, persist_mapping=False, defroute=True,
|
|
||||||
dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
name_vlan = '%s%i' % (name, vlan_id)
|
|
||||||
super(IvsInterface, self).__init__(name_vlan, use_dhcp, use_dhcpv6,
|
|
||||||
addresses, routes, mtu, primary,
|
|
||||||
nic_mapping, persist_mapping,
|
|
||||||
defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
self.vlan_id = int(vlan_id)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = json.get('name')
|
|
||||||
vlan_id = _get_required_field(json, 'vlan_id', 'IvsInterface')
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return IvsInterface(vlan_id, name, *opts)
|
|
||||||
|
|
||||||
|
|
||||||
class NfvswitchInternal(_BaseOpts):
|
|
||||||
"""Base class for nfvswitch internal interfaces."""
|
|
||||||
|
|
||||||
def __init__(self, vlan_id, name='nfvswitch', use_dhcp=False,
|
|
||||||
use_dhcpv6=False, addresses=None, routes=None, mtu=1500,
|
|
||||||
primary=False, nic_mapping=None, persist_mapping=False,
|
|
||||||
defroute=True, dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
name_vlan = '%s%i' % (name, vlan_id)
|
|
||||||
super(NfvswitchInternal, self).__init__(name_vlan, use_dhcp,
|
|
||||||
use_dhcpv6, addresses, routes,
|
|
||||||
mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers)
|
|
||||||
self.vlan_id = int(vlan_id)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = json.get('name')
|
|
||||||
vlan_id = _get_required_field(json, 'vlan_id', 'NfvswitchInternal')
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return NfvswitchInternal(vlan_id, name, *opts)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsBridge(_BaseOpts):
|
|
||||||
"""Base class for OVS bridges."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, members=None, ovs_options=None,
|
|
||||||
ovs_extra=None, nic_mapping=None, persist_mapping=False,
|
|
||||||
defroute=True, dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
ovs_extra = ovs_extra or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(OvsBridge, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, False, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers)
|
|
||||||
self.members = members
|
|
||||||
self.ovs_options = ovs_options
|
|
||||||
self.ovs_extra = ovs_extra
|
|
||||||
for member in self.members:
|
|
||||||
member.bridge_name = name
|
|
||||||
if not isinstance(member, OvsTunnel):
|
|
||||||
member.ovs_port = True
|
|
||||||
if member.primary:
|
|
||||||
if self.primary_interface_name:
|
|
||||||
msg = 'Only one primary interface allowed per bridge.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
if member.primary_interface_name:
|
|
||||||
self.primary_interface_name = member.primary_interface_name
|
|
||||||
else:
|
|
||||||
self.primary_interface_name = member.name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'OvsBridge')
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
ovs_options = json.get('ovs_options')
|
|
||||||
ovs_extra = json.get('ovs_extra', [])
|
|
||||||
members = []
|
|
||||||
|
|
||||||
# members
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return OvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, ovs_options=ovs_options,
|
|
||||||
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping, defroute=defroute,
|
|
||||||
dhclient_args=dhclient_args, dns_servers=dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class LinuxBridge(_BaseOpts):
|
|
||||||
"""Base class for Linux bridges."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, members=None, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(LinuxBridge, self).__init__(name, use_dhcp, use_dhcpv6,
|
|
||||||
addresses, routes, mtu, False,
|
|
||||||
nic_mapping, persist_mapping,
|
|
||||||
defroute, dhclient_args, dns_servers)
|
|
||||||
self.members = members
|
|
||||||
for member in self.members:
|
|
||||||
member.linux_bridge_name = name
|
|
||||||
member.ovs_port = False
|
|
||||||
if member.primary:
|
|
||||||
if self.primary_interface_name:
|
|
||||||
msg = 'Only one primary interface allowed per bridge.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
if member.primary_interface_name:
|
|
||||||
self.primary_interface_name = member.primary_interface_name
|
|
||||||
else:
|
|
||||||
self.primary_interface_name = member.name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'LinuxBridge')
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
members = []
|
|
||||||
|
|
||||||
# members
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return LinuxBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping, defroute=defroute,
|
|
||||||
dhclient_args=dhclient_args,
|
|
||||||
dns_servers=dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class IvsBridge(_BaseOpts):
|
|
||||||
"""Base class for IVS bridges.
|
|
||||||
|
|
||||||
Indigo Virtual Switch (IVS) is a virtual switch for Linux.
|
|
||||||
It is compatible with the KVM hypervisor and leveraging the
|
|
||||||
Open vSwitch kernel module for packet forwarding. There are
|
|
||||||
three major differences between IVS and OVS:
|
|
||||||
1. Each node can have at most one ivs, no name required.
|
|
||||||
2. Bond is not allowed to attach to an ivs. It is the SDN
|
|
||||||
controller's job to dynamically form bonds on ivs.
|
|
||||||
3. IP address can only be statically assigned.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name='ivs', use_dhcp=False, use_dhcpv6=False,
|
|
||||||
addresses=None, routes=None, mtu=1500, members=None,
|
|
||||||
nic_mapping=None, persist_mapping=False, defroute=True,
|
|
||||||
dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(IvsBridge, self).__init__(name, use_dhcp, use_dhcpv6,
|
|
||||||
addresses, routes, mtu, False,
|
|
||||||
nic_mapping, persist_mapping,
|
|
||||||
defroute, dhclient_args, dns_servers)
|
|
||||||
self.members = members
|
|
||||||
for member in self.members:
|
|
||||||
if isinstance(member, OvsBond) or isinstance(member, LinuxBond):
|
|
||||||
msg = 'IVS does not support bond interfaces.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
member.ivs_bridge_name = name
|
|
||||||
member.ovs_port = False
|
|
||||||
self.primary_interface_name = None # ivs doesn't use primary intf
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = 'ivs'
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
members = []
|
|
||||||
|
|
||||||
# members
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return IvsBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping, defroute=defroute,
|
|
||||||
dhclient_args=dhclient_args,
|
|
||||||
dns_servers=dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class NfvswitchBridge(_BaseOpts):
|
|
||||||
"""Base class for NFVSwitch bridges.
|
|
||||||
|
|
||||||
NFVSwitch is a virtual switch for Linux.
|
|
||||||
It is compatible with the KVM hypervisor and uses DPDK for packet
|
|
||||||
forwarding.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, name='nfvswitch', use_dhcp=False, use_dhcpv6=False,
|
|
||||||
addresses=None, routes=None, mtu=1500, members=None,
|
|
||||||
nic_mapping=None, persist_mapping=False, defroute=True,
|
|
||||||
dhclient_args=None, dns_servers=None, cpus=""):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(NfvswitchBridge, self).__init__(name, use_dhcp, use_dhcpv6,
|
|
||||||
addresses, routes, mtu, False,
|
|
||||||
nic_mapping, persist_mapping,
|
|
||||||
defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
self.cpus = cpus
|
|
||||||
self.members = members
|
|
||||||
for member in self.members:
|
|
||||||
if isinstance(member, OvsBond) or isinstance(member, LinuxBond):
|
|
||||||
msg = 'NFVSwitch does not support bond interfaces.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
member.nfvswitch_bridge_name = name
|
|
||||||
member.ovs_port = False
|
|
||||||
self.primary_interface_name = None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = 'nfvswitch'
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
|
|
||||||
# members
|
|
||||||
members = []
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
cpus = ''
|
|
||||||
cpus_json = json.get('cpus')
|
|
||||||
if cpus_json:
|
|
||||||
if isinstance(cpus_json, basestring):
|
|
||||||
cpus = cpus_json
|
|
||||||
else:
|
|
||||||
msg = '"cpus" must be a string of numbers separated by commas.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
else:
|
|
||||||
msg = 'Config "cpus" is mandatory.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return NfvswitchBridge(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping,
|
|
||||||
defroute=defroute, dhclient_args=dhclient_args,
|
|
||||||
dns_servers=dns_servers, cpus=cpus)
|
|
||||||
|
|
||||||
|
|
||||||
class LinuxTeam(_BaseOpts):
|
|
||||||
"""Base class for Linux bonds using teamd."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, members=None,
|
|
||||||
bonding_options=None, nic_mapping=None, persist_mapping=False,
|
|
||||||
defroute=True, dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(LinuxTeam, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers)
|
|
||||||
self.members = members
|
|
||||||
self.bonding_options = bonding_options
|
|
||||||
for member in self.members:
|
|
||||||
member.linux_team_name = name
|
|
||||||
if member.primary:
|
|
||||||
if self.primary_interface_name:
|
|
||||||
msg = 'Only one primary interface allowed per team.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
if member.primary_interface_name:
|
|
||||||
self.primary_interface_name = member.primary_interface_name
|
|
||||||
else:
|
|
||||||
self.primary_interface_name = member.name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'LinuxTeam')
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
bonding_options = json.get('bonding_options')
|
|
||||||
members = []
|
|
||||||
|
|
||||||
# members
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return LinuxTeam(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, bonding_options=bonding_options,
|
|
||||||
nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping, defroute=defroute,
|
|
||||||
dhclient_args=dhclient_args, dns_servers=dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class LinuxBond(_BaseOpts):
|
|
||||||
"""Base class for Linux bonds."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, members=None,
|
|
||||||
bonding_options=None, nic_mapping=None, persist_mapping=False,
|
|
||||||
defroute=True, dhclient_args=None, dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(LinuxBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers)
|
|
||||||
self.members = members
|
|
||||||
self.bonding_options = bonding_options
|
|
||||||
for member in self.members:
|
|
||||||
member.linux_bond_name = name
|
|
||||||
if member.primary:
|
|
||||||
if self.primary_interface_name:
|
|
||||||
msg = 'Only one primary interface allowed per bond.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
if member.primary_interface_name:
|
|
||||||
self.primary_interface_name = member.primary_interface_name
|
|
||||||
else:
|
|
||||||
self.primary_interface_name = member.name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'LinuxBond')
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
bonding_options = json.get('bonding_options')
|
|
||||||
members = []
|
|
||||||
|
|
||||||
# members
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return LinuxBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, bonding_options=bonding_options,
|
|
||||||
nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping, defroute=defroute,
|
|
||||||
dhclient_args=dhclient_args, dns_servers=dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsBond(_BaseOpts):
|
|
||||||
"""Base class for OVS bonds."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, members=None,
|
|
||||||
ovs_options=None, ovs_extra=None, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
members = members or []
|
|
||||||
ovs_extra = ovs_extra or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(OvsBond, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
self.members = members
|
|
||||||
self.ovs_options = ovs_options
|
|
||||||
self.ovs_extra = ovs_extra
|
|
||||||
for member in self.members:
|
|
||||||
if member.primary:
|
|
||||||
if self.primary_interface_name:
|
|
||||||
msg = 'Only one primary interface allowed per bond.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
if member.primary_interface_name:
|
|
||||||
self.primary_interface_name = member.primary_interface_name
|
|
||||||
else:
|
|
||||||
self.primary_interface_name = member.name
|
|
||||||
if not self.primary_interface_name:
|
|
||||||
bond_members = list(self.members)
|
|
||||||
bond_members.sort(key=lambda x: x.name)
|
|
||||||
self.primary_interface_name = bond_members[0].name
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'OvsBond')
|
|
||||||
(use_dhcp, use_dhcpv6, addresses, routes, mtu, nic_mapping,
|
|
||||||
persist_mapping, defroute, dhclient_args,
|
|
||||||
dns_servers) = _BaseOpts.base_opts_from_json(
|
|
||||||
json, include_primary=False)
|
|
||||||
ovs_options = json.get('ovs_options')
|
|
||||||
ovs_extra = json.get('ovs_extra', [])
|
|
||||||
members = []
|
|
||||||
|
|
||||||
# members
|
|
||||||
members_json = json.get('members')
|
|
||||||
if members_json:
|
|
||||||
if isinstance(members_json, list):
|
|
||||||
for member in members_json:
|
|
||||||
members.append(object_from_json(member))
|
|
||||||
else:
|
|
||||||
msg = 'Members must be a list.'
|
|
||||||
raise InvalidConfigException(msg)
|
|
||||||
|
|
||||||
return OvsBond(name, use_dhcp=use_dhcp, use_dhcpv6=use_dhcpv6,
|
|
||||||
addresses=addresses, routes=routes, mtu=mtu,
|
|
||||||
members=members, ovs_options=ovs_options,
|
|
||||||
ovs_extra=ovs_extra, nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=persist_mapping, defroute=defroute,
|
|
||||||
dhclient_args=dhclient_args, dns_servers=dns_servers)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsTunnel(_BaseOpts):
|
|
||||||
"""Base class for OVS Tunnels."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None, tunnel_type=None, ovs_options=None,
|
|
||||||
ovs_extra=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
ovs_extra = ovs_extra or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(OvsTunnel, self).__init__(name, use_dhcp, use_dhcpv6, addresses,
|
|
||||||
routes, mtu, primary, nic_mapping,
|
|
||||||
persist_mapping, defroute,
|
|
||||||
dhclient_args, dns_servers)
|
|
||||||
self.tunnel_type = tunnel_type
|
|
||||||
self.ovs_options = ovs_options or []
|
|
||||||
self.ovs_extra = ovs_extra or []
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'OvsTunnel')
|
|
||||||
tunnel_type = _get_required_field(json, 'tunnel_type', 'OvsTunnel')
|
|
||||||
ovs_options = json.get('ovs_options', [])
|
|
||||||
ovs_options = ['options:%s' % opt for opt in ovs_options]
|
|
||||||
ovs_extra = json.get('ovs_extra', [])
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return OvsTunnel(name, *opts, tunnel_type=tunnel_type,
|
|
||||||
ovs_options=ovs_options, ovs_extra=ovs_extra)
|
|
||||||
|
|
||||||
|
|
||||||
class OvsPatchPort(_BaseOpts):
|
|
||||||
"""Base class for OVS Patch Ports."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None, bridge_name=None, peer=None,
|
|
||||||
ovs_options=None, ovs_extra=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
ovs_extra = ovs_extra or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(OvsPatchPort, self).__init__(name, use_dhcp, use_dhcpv6,
|
|
||||||
addresses, routes, mtu, primary,
|
|
||||||
nic_mapping, persist_mapping,
|
|
||||||
defroute, dhclient_args,
|
|
||||||
dns_servers)
|
|
||||||
self.bridge_name = bridge_name
|
|
||||||
self.peer = peer
|
|
||||||
self.ovs_options = ovs_options or []
|
|
||||||
self.ovs_extra = ovs_extra or []
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'OvsPatchPort')
|
|
||||||
bridge_name = _get_required_field(json, 'bridge_name', 'OvsPatchPort')
|
|
||||||
peer = _get_required_field(json, 'peer', 'OvsPatchPort')
|
|
||||||
ovs_options = json.get('ovs_options', [])
|
|
||||||
ovs_options = ['options:%s' % opt for opt in ovs_options]
|
|
||||||
ovs_extra = json.get('ovs_extra', [])
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return OvsPatchPort(name, *opts, bridge_name=bridge_name, peer=peer,
|
|
||||||
ovs_options=ovs_options, ovs_extra=ovs_extra)
|
|
||||||
|
|
||||||
|
|
||||||
class IbInterface(_BaseOpts):
|
|
||||||
"""Base class for InfiniBand network interfaces."""
|
|
||||||
|
|
||||||
def __init__(self, name, use_dhcp=False, use_dhcpv6=False, addresses=None,
|
|
||||||
routes=None, mtu=None, primary=False, nic_mapping=None,
|
|
||||||
persist_mapping=False, defroute=True, dhclient_args=None,
|
|
||||||
dns_servers=None):
|
|
||||||
addresses = addresses or []
|
|
||||||
routes = routes or []
|
|
||||||
dns_servers = dns_servers or []
|
|
||||||
super(IbInterface, self).__init__(name, use_dhcp, use_dhcpv6,
|
|
||||||
addresses, routes, mtu, primary,
|
|
||||||
nic_mapping, persist_mapping,
|
|
||||||
defroute, dhclient_args, dns_servers)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def from_json(json):
|
|
||||||
name = _get_required_field(json, 'name', 'IbInterface')
|
|
||||||
opts = _BaseOpts.base_opts_from_json(json)
|
|
||||||
return IbInterface(name, *opts)
|
|
@ -1,70 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2010-2011 OpenStack Foundation
|
|
||||||
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
import fixtures
|
|
||||||
import stubout
|
|
||||||
import testtools
|
|
||||||
|
|
||||||
from os_net_config import objects
|
|
||||||
|
|
||||||
_TRUE_VALUES = ('True', 'true', '1', 'yes')
|
|
||||||
|
|
||||||
|
|
||||||
class TestCase(testtools.TestCase):
|
|
||||||
|
|
||||||
"""Test case base class for all unit tests."""
|
|
||||||
stub_numbered_nics = True
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
"""Run before each test method to initialize test environment."""
|
|
||||||
|
|
||||||
super(TestCase, self).setUp()
|
|
||||||
self.stubs = stubout.StubOutForTesting()
|
|
||||||
self.stubbed_numbered_nics = {}
|
|
||||||
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return self.stubbed_numbered_nics
|
|
||||||
if self.stub_numbered_nics:
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
test_timeout = os.environ.get('OS_TEST_TIMEOUT', 0)
|
|
||||||
try:
|
|
||||||
test_timeout = int(test_timeout)
|
|
||||||
except ValueError:
|
|
||||||
# If timeout value is invalid do not set a timeout.
|
|
||||||
test_timeout = 0
|
|
||||||
if test_timeout > 0:
|
|
||||||
self.useFixture(fixtures.Timeout(test_timeout, gentle=True))
|
|
||||||
|
|
||||||
self.useFixture(fixtures.NestedTempfile())
|
|
||||||
self.useFixture(fixtures.TempHomeDir())
|
|
||||||
|
|
||||||
if os.environ.get('OS_STDOUT_CAPTURE') in _TRUE_VALUES:
|
|
||||||
stdout = self.useFixture(fixtures.StringStream('stdout')).stream
|
|
||||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', stdout))
|
|
||||||
if os.environ.get('OS_STDERR_CAPTURE') in _TRUE_VALUES:
|
|
||||||
stderr = self.useFixture(fixtures.StringStream('stderr')).stream
|
|
||||||
self.useFixture(fixtures.MonkeyPatch('sys.stderr', stderr))
|
|
||||||
|
|
||||||
self.log_fixture = self.useFixture(fixtures.FakeLogger())
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.stubs.UnsetAll()
|
|
||||||
self.stubs.SmartUnsetAll()
|
|
||||||
super(TestCase, self).tearDown()
|
|
@ -1,166 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
import sys
|
|
||||||
|
|
||||||
import os_net_config
|
|
||||||
from os_net_config import cli
|
|
||||||
from os_net_config import impl_ifcfg
|
|
||||||
from os_net_config.tests import base
|
|
||||||
import six
|
|
||||||
|
|
||||||
|
|
||||||
REALPATH = os.path.dirname(os.path.realpath(__file__))
|
|
||||||
SAMPLE_BASE = os.path.join(REALPATH, '../../', 'etc',
|
|
||||||
'os-net-config', 'samples')
|
|
||||||
|
|
||||||
|
|
||||||
class TestCli(base.TestCase):
|
|
||||||
|
|
||||||
def run_cli(self, argstr, exitcodes=(0,)):
|
|
||||||
orig = sys.stdout
|
|
||||||
orig_stderr = sys.stderr
|
|
||||||
|
|
||||||
sys.stdout = six.StringIO()
|
|
||||||
sys.stderr = six.StringIO()
|
|
||||||
ret = cli.main(argstr.split())
|
|
||||||
self.assertIn(ret, exitcodes)
|
|
||||||
|
|
||||||
stdout = sys.stdout.getvalue()
|
|
||||||
sys.stdout.close()
|
|
||||||
sys.stdout = orig
|
|
||||||
stderr = sys.stderr.getvalue()
|
|
||||||
sys.stderr.close()
|
|
||||||
sys.stderr = orig_stderr
|
|
||||||
return (stdout, stderr)
|
|
||||||
|
|
||||||
def test_bond_noop_output(self):
|
|
||||||
bond_yaml = os.path.join(SAMPLE_BASE, 'bond.yaml')
|
|
||||||
bond_json = os.path.join(SAMPLE_BASE, 'bond.json')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
|
|
||||||
'-c %s' % bond_yaml)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
|
|
||||||
'-c %s' % bond_json)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
sanity_devices = ['DEVICE=br-ctlplane',
|
|
||||||
'DEVICE=em2',
|
|
||||||
'DEVICE=em1',
|
|
||||||
'DEVICE=bond1',
|
|
||||||
'DEVICETYPE=ovs']
|
|
||||||
for dev in sanity_devices:
|
|
||||||
self.assertIn(dev, stdout_yaml)
|
|
||||||
self.assertEqual(stdout_yaml, stdout_json)
|
|
||||||
|
|
||||||
def test_bridge_noop_output(self):
|
|
||||||
bridge_yaml = os.path.join(SAMPLE_BASE, 'bridge_dhcp.yaml')
|
|
||||||
bridge_json = os.path.join(SAMPLE_BASE, 'bridge_dhcp.json')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=eni --noop -c %s' %
|
|
||||||
bridge_yaml)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
stdout_json, stderr = self.run_cli('ARG0 --provider=eni --noop -c %s' %
|
|
||||||
bridge_json)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
sanity_devices = ['iface br-ctlplane inet dhcp',
|
|
||||||
'iface em1',
|
|
||||||
'ovs_type OVSBridge']
|
|
||||||
for dev in sanity_devices:
|
|
||||||
self.assertIn(dev, stdout_yaml)
|
|
||||||
self.assertEqual(stdout_yaml, stdout_json)
|
|
||||||
|
|
||||||
def test_vlan_noop_output(self):
|
|
||||||
vlan_yaml = os.path.join(SAMPLE_BASE, 'bridge_vlan.yaml')
|
|
||||||
vlan_json = os.path.join(SAMPLE_BASE, 'bridge_vlan.json')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop -c %s'
|
|
||||||
% vlan_yaml)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop -c %s'
|
|
||||||
% vlan_json)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
sanity_devices = ['DEVICE=br-ctlplane',
|
|
||||||
'DEVICE=em1',
|
|
||||||
'DEVICE=vlan16',
|
|
||||||
'DEVICETYPE=ovs']
|
|
||||||
for dev in sanity_devices:
|
|
||||||
self.assertIn(dev, stdout_yaml)
|
|
||||||
self.assertEqual(stdout_yaml, stdout_json)
|
|
||||||
|
|
||||||
def test_interface_noop_output(self):
|
|
||||||
interface_yaml = os.path.join(SAMPLE_BASE, 'interface.yaml')
|
|
||||||
interface_json = os.path.join(SAMPLE_BASE, 'interface.json')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop -c %s'
|
|
||||||
% interface_yaml)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop -c %s'
|
|
||||||
% interface_json)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
sanity_devices = ['DEVICE=em1',
|
|
||||||
'BOOTPROTO=static',
|
|
||||||
'IPADDR=192.0.2.1']
|
|
||||||
for dev in sanity_devices:
|
|
||||||
self.assertIn(dev, stdout_yaml)
|
|
||||||
self.assertEqual(stdout_yaml, stdout_json)
|
|
||||||
|
|
||||||
def test_bridge_noop_rootfs(self):
|
|
||||||
for provider in ('ifcfg', 'eni'):
|
|
||||||
bond_yaml = os.path.join(SAMPLE_BASE, 'bridge_dhcp.yaml')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=%s --noop '
|
|
||||||
'--root-dir=/rootfs '
|
|
||||||
'-c %s' % (provider, bond_yaml))
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
self.assertIn('File: /rootfs/', stdout_yaml)
|
|
||||||
|
|
||||||
def test_interface_noop_detailed_exit_codes(self):
|
|
||||||
interface_yaml = os.path.join(SAMPLE_BASE, 'interface.yaml')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
|
|
||||||
'-c %s --detailed-exit-codes'
|
|
||||||
% interface_yaml, exitcodes=(2,))
|
|
||||||
|
|
||||||
def test_interface_noop_detailed_exit_codes_no_changes(self):
|
|
||||||
interface_yaml = os.path.join(SAMPLE_BASE, 'interface.yaml')
|
|
||||||
|
|
||||||
class TestImpl(os_net_config.NetConfig):
|
|
||||||
|
|
||||||
def add_interface(self, interface):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def apply(self, cleanup=False, activate=True):
|
|
||||||
# this fake implementation returns no changes
|
|
||||||
return {}
|
|
||||||
|
|
||||||
self.stubs.Set(impl_ifcfg, 'IfcfgNetConfig', TestImpl)
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
|
|
||||||
'-c %s --detailed-exit-codes'
|
|
||||||
% interface_yaml, exitcodes=(0,))
|
|
||||||
|
|
||||||
def test_nfvswitch_noop_output(self):
|
|
||||||
nfvswitch_yaml = os.path.join(SAMPLE_BASE, 'nfvswitch.yaml')
|
|
||||||
nfvswitch_json = os.path.join(SAMPLE_BASE, 'nfvswitch.json')
|
|
||||||
stdout_yaml, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
|
|
||||||
'-c %s' % nfvswitch_yaml)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
stdout_json, stderr = self.run_cli('ARG0 --provider=ifcfg --noop '
|
|
||||||
'-c %s' % nfvswitch_json)
|
|
||||||
self.assertEqual('', stderr)
|
|
||||||
sanity_devices = ['DEVICE=nic2',
|
|
||||||
'DEVICE=nic3',
|
|
||||||
'DEVICE=api201',
|
|
||||||
'DEVICE=storage202',
|
|
||||||
'DEVICETYPE=nfvswitch']
|
|
||||||
for dev in sanity_devices:
|
|
||||||
self.assertIn(dev, stdout_yaml)
|
|
||||||
self.assertEqual(stdout_yaml, stdout_json)
|
|
@ -1,297 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils
|
|
||||||
|
|
||||||
from os_net_config import impl_eni
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config.tests import base
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
_AUTO = "auto eth0\n"
|
|
||||||
|
|
||||||
_v4_IFACE_NO_IP = _AUTO + "iface eth0 inet manual\n"
|
|
||||||
|
|
||||||
_V4_IFACE_STATIC_IP = _AUTO + """iface eth0 inet static
|
|
||||||
address 192.168.1.2
|
|
||||||
netmask 255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V4_IFACE_STATIC_IP_MULTIPLE = (_V4_IFACE_STATIC_IP + _AUTO +
|
|
||||||
"""iface eth0 inet static
|
|
||||||
address 10.0.0.2
|
|
||||||
netmask 255.0.0.0
|
|
||||||
""")
|
|
||||||
|
|
||||||
_V6_IFACE_STATIC_IP = _AUTO + """iface eth0 inet6 static
|
|
||||||
address fe80::2677:3ff:fe7d:4c
|
|
||||||
netmask 128
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V6_IFACE_STATIC_IP_MULTIPLE = (_V6_IFACE_STATIC_IP + _AUTO +
|
|
||||||
"""iface eth0 inet6 static
|
|
||||||
address 2001:abcd::2
|
|
||||||
netmask 64
|
|
||||||
""")
|
|
||||||
|
|
||||||
_IFACE_DHCP = _AUTO + "iface eth0 inet dhcp\n"
|
|
||||||
|
|
||||||
_OVS_PORT_BASE = _AUTO + "allow-br0 eth0\n"
|
|
||||||
|
|
||||||
_OVS_PORT_IFACE = _OVS_PORT_BASE + """iface eth0 inet manual
|
|
||||||
ovs_bridge br0
|
|
||||||
ovs_type OVSPort
|
|
||||||
"""
|
|
||||||
|
|
||||||
_OVS_BRIDGE_DHCP = """auto br0
|
|
||||||
allow-ovs br0
|
|
||||||
iface br0 inet dhcp
|
|
||||||
ovs_type OVSBridge
|
|
||||||
ovs_ports eth0
|
|
||||||
pre-up ip addr flush dev eth0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE = _OVS_BRIDGE_DHCP + \
|
|
||||||
" ovs_extra set bridge br0 other-config:hwaddr=a1:b2:c3:d4:e5\n"
|
|
||||||
|
|
||||||
|
|
||||||
_OVS_BRIDGE_DHCP_OVS_EXTRA = _OVS_BRIDGE_DHCP + \
|
|
||||||
" ovs_extra set bridge br0 other-config:hwaddr=a1:b2:c3:d4:e5" + \
|
|
||||||
" -- br-set-external-id br-ctlplane bridge-id br-ctlplane\n"
|
|
||||||
|
|
||||||
|
|
||||||
_VLAN_NO_IP = """auto vlan5
|
|
||||||
iface vlan5 inet manual
|
|
||||||
vlan-raw-device eth0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_VLAN_OVS_PORT = """auto vlan5
|
|
||||||
allow-br0 vlan5
|
|
||||||
iface vlan5 inet manual
|
|
||||||
ovs_bridge br0
|
|
||||||
ovs_type OVSIntPort
|
|
||||||
ovs_options tag=5
|
|
||||||
"""
|
|
||||||
|
|
||||||
_RTS = """up route add -net 172.19.0.0 netmask 255.255.255.0 gw 192.168.1.1
|
|
||||||
down route del -net 172.19.0.0 netmask 255.255.255.0 gw 192.168.1.1
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class TestENINetConfig(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestENINetConfig, self).setUp()
|
|
||||||
|
|
||||||
self.provider = impl_eni.ENINetConfig()
|
|
||||||
self.if_name = 'eth0'
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
super(TestENINetConfig, self).tearDown()
|
|
||||||
|
|
||||||
def get_interface_config(self, name="eth0"):
|
|
||||||
return self.provider.interfaces[name]
|
|
||||||
|
|
||||||
def get_route_config(self):
|
|
||||||
return self.provider.routes[self.if_name]
|
|
||||||
|
|
||||||
def _default_interface(self, addr=[], rts=[]):
|
|
||||||
return objects.Interface(self.if_name, addresses=addr, routes=rts)
|
|
||||||
|
|
||||||
def test_interface_no_ip(self):
|
|
||||||
interface = self._default_interface()
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_v4_IFACE_NO_IP, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_v4(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = self._default_interface([v4_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFACE_STATIC_IP, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_v4_multiple(self):
|
|
||||||
v4_addresses = [objects.Address('192.168.1.2/24'),
|
|
||||||
objects.Address('10.0.0.2/8')]
|
|
||||||
interface = self._default_interface(v4_addresses)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFACE_STATIC_IP_MULTIPLE,
|
|
||||||
self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_v6(self):
|
|
||||||
v6_addr = objects.Address('fe80::2677:3ff:fe7d:4c')
|
|
||||||
interface = self._default_interface([v6_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V6_IFACE_STATIC_IP, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_v6_multiple(self):
|
|
||||||
v6_addresses = [objects.Address('fe80::2677:3ff:fe7d:4c'),
|
|
||||||
objects.Address('2001:abcd::2/64')]
|
|
||||||
interface = self._default_interface(v6_addresses)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V6_IFACE_STATIC_IP_MULTIPLE,
|
|
||||||
self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_dhcp(self):
|
|
||||||
interface = self._default_interface()
|
|
||||||
interface.use_dhcp = True
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_IFACE_DHCP, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_both_v4_and_v6(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
v6_addr = objects.Address('fe80::2677:3ff:fe7d:4c')
|
|
||||||
interface = self._default_interface([v4_addr, v6_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFACE_STATIC_IP + _V6_IFACE_STATIC_IP,
|
|
||||||
self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_ovs_port_interface(self):
|
|
||||||
interface = self._default_interface()
|
|
||||||
interface.ovs_port = True
|
|
||||||
interface.bridge_name = 'br0'
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_network_with_routes(self):
|
|
||||||
route1 = objects.Route('192.168.1.1', '172.19.0.0/24')
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = self._default_interface([v4_addr], [route1])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFACE_STATIC_IP, self.get_interface_config())
|
|
||||||
self.assertEqual(_RTS, self.get_route_config())
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_dhcp(self):
|
|
||||||
interface = self._default_interface()
|
|
||||||
bridge = objects.OvsBridge('br0', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP, self.provider.bridges['br0'])
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_dhcp_and_primary_interface(self):
|
|
||||||
|
|
||||||
def test_interface_mac(name):
|
|
||||||
return "a1:b2:c3:d4:e5"
|
|
||||||
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
|
|
||||||
|
|
||||||
interface = objects.Interface(self.if_name, primary=True)
|
|
||||||
bridge = objects.OvsBridge('br0', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE,
|
|
||||||
self.provider.bridges['br0'])
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_dhcp_and_primary_with_ovs_extra(self):
|
|
||||||
|
|
||||||
def test_interface_mac(name):
|
|
||||||
return "a1:b2:c3:d4:e5"
|
|
||||||
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
|
|
||||||
|
|
||||||
interface = objects.Interface(self.if_name, primary=True)
|
|
||||||
ovs_extra = "br-set-external-id br-ctlplane bridge-id br-ctlplane"
|
|
||||||
bridge = objects.OvsBridge('br0', use_dhcp=True,
|
|
||||||
members=[interface],
|
|
||||||
ovs_extra=[ovs_extra])
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_PORT_IFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
|
|
||||||
self.provider.bridges['br0'])
|
|
||||||
|
|
||||||
def test_vlan(self):
|
|
||||||
vlan = objects.Vlan('eth0', 5)
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.assertEqual(_VLAN_NO_IP, self.get_interface_config('vlan5'))
|
|
||||||
|
|
||||||
def test_vlan_mtu_1500(self):
|
|
||||||
vlan = objects.Vlan('eth0', 5, mtu=1500)
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
expected = _VLAN_NO_IP + ' mtu 1500\n'
|
|
||||||
self.assertEqual(expected, self.get_interface_config('vlan5'))
|
|
||||||
|
|
||||||
def test_vlan_ovs_bridge_int_port(self):
|
|
||||||
vlan = objects.Vlan('eth0', 5)
|
|
||||||
bridge = objects.OvsBridge('br0', use_dhcp=True,
|
|
||||||
members=[vlan])
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.assertEqual(_VLAN_OVS_PORT, self.get_interface_config('vlan5'))
|
|
||||||
|
|
||||||
|
|
||||||
class TestENINetConfigApply(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestENINetConfigApply, self).setUp()
|
|
||||||
self.temp_config_file = tempfile.NamedTemporaryFile()
|
|
||||||
self.ifup_interface_names = []
|
|
||||||
|
|
||||||
def test_config_path(prefix):
|
|
||||||
return self.temp_config_file.name
|
|
||||||
self.stubs.Set(impl_eni, '_network_config_path', test_config_path)
|
|
||||||
|
|
||||||
def test_execute(*args, **kwargs):
|
|
||||||
if args[0] == '/sbin/ifup':
|
|
||||||
self.ifup_interface_names.append(args[1])
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.stubs.Set(processutils, 'execute', test_execute)
|
|
||||||
|
|
||||||
self.provider = impl_eni.ENINetConfig()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.temp_config_file.close()
|
|
||||||
super(TestENINetConfigApply, self).tearDown()
|
|
||||||
|
|
||||||
def test_network_apply(self):
|
|
||||||
route = objects.Route('192.168.1.1', '172.19.0.0/24')
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('eth0', addresses=[v4_addr],
|
|
||||||
routes=[route])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
|
|
||||||
self.provider.apply()
|
|
||||||
iface_data = utils.get_file_data(self.temp_config_file.name)
|
|
||||||
self.assertEqual((_V4_IFACE_STATIC_IP + _RTS), iface_data)
|
|
||||||
self.assertIn('eth0', self.ifup_interface_names)
|
|
||||||
|
|
||||||
def test_apply_noactivate(self):
|
|
||||||
route = objects.Route('192.168.1.1', '172.19.0.0/24')
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('eth0', addresses=[v4_addr],
|
|
||||||
routes=[route])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
|
|
||||||
self.provider.apply(activate=False)
|
|
||||||
iface_data = utils.get_file_data(self.temp_config_file.name)
|
|
||||||
self.assertEqual((_V4_IFACE_STATIC_IP + _RTS), iface_data)
|
|
||||||
self.assertEqual([], self.ifup_interface_names)
|
|
||||||
|
|
||||||
def test_dhcp_ovs_bridge_network_apply(self):
|
|
||||||
interface = objects.Interface('eth0')
|
|
||||||
bridge = objects.OvsBridge('br0', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
iface_data = utils.get_file_data(self.temp_config_file.name)
|
|
||||||
self.assertEqual((_OVS_BRIDGE_DHCP + _OVS_PORT_IFACE), iface_data)
|
|
||||||
self.assertIn('eth0', self.ifup_interface_names)
|
|
||||||
self.assertIn('br0', self.ifup_interface_names)
|
|
@ -1,976 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from oslo_concurrency import processutils
|
|
||||||
|
|
||||||
from os_net_config import impl_ifcfg
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config.tests import base
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
_BASE_IFCFG = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
"""
|
|
||||||
|
|
||||||
_NO_IP = _BASE_IFCFG + "BOOTPROTO=none\n"
|
|
||||||
|
|
||||||
_V4_IFCFG = _BASE_IFCFG + """BOOTPROTO=static
|
|
||||||
IPADDR=192.168.1.2
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V4_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
|
|
||||||
BOOTPROTO=static
|
|
||||||
IPADDR=192.168.1.2
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
IPV6_AUTOCONF=no
|
|
||||||
IPV6ADDR=2001:abc:a::/64
|
|
||||||
"""
|
|
||||||
|
|
||||||
_IFCFG_VLAN = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1.120
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
VLAN=yes
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V4_IFCFG_MAPPED = _V4_IFCFG.replace('em1', 'nic1') + "HWADDR=a1:b2:c3:d4:e5\n"
|
|
||||||
|
|
||||||
|
|
||||||
_BASE_IB_IFCFG = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=ib0
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
TYPE=Infiniband
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V4_IB_IFCFG = _BASE_IB_IFCFG + """BOOTPROTO=static
|
|
||||||
IPADDR=192.168.1.2
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V4_IFCFG_MULTIPLE = _V4_IFCFG + """IPADDR1=192.168.1.3
|
|
||||||
NETMASK1=255.255.255.255
|
|
||||||
IPADDR2=10.0.0.2
|
|
||||||
NETMASK2=255.0.0.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_IB_V4_IFCFG_MULTIPLE = _V4_IB_IFCFG + """IPADDR1=192.168.1.3
|
|
||||||
NETMASK1=255.255.255.255
|
|
||||||
IPADDR2=10.0.0.2
|
|
||||||
NETMASK2=255.0.0.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V6_IFCFG = _BASE_IFCFG + """IPV6INIT=yes
|
|
||||||
IPV6_AUTOCONF=no
|
|
||||||
IPV6ADDR=2001:abc:a::/64
|
|
||||||
"""
|
|
||||||
|
|
||||||
_V6_IFCFG_MULTIPLE = (_V6_IFCFG + "IPV6ADDR_SECONDARIES=\"2001:abc:b::1/64 " +
|
|
||||||
"2001:abc:c::2/96\"\n")
|
|
||||||
|
|
||||||
_OVS_IFCFG = _BASE_IFCFG + "DEVICETYPE=ovs\nBOOTPROTO=none\n"
|
|
||||||
|
|
||||||
_OVS_IFCFG_TUNNEL = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=tun0
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
DEVICETYPE=ovs
|
|
||||||
TYPE=OVSTunnel
|
|
||||||
OVS_BRIDGE=br-ctlplane
|
|
||||||
OVS_TUNNEL_TYPE=gre
|
|
||||||
OVS_TUNNEL_OPTIONS="options:remote_ip=192.168.1.1"
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_OVS_BRIDGE_IFCFG = _BASE_IFCFG + "DEVICETYPE=ovs\n"
|
|
||||||
|
|
||||||
_LINUX_BRIDGE_IFCFG = _BASE_IFCFG + "BRIDGE=br-ctlplane\nBOOTPROTO=none\n"
|
|
||||||
|
|
||||||
_ROUTES = """default via 192.168.1.1 dev em1
|
|
||||||
172.19.0.0/24 via 192.168.1.1 dev em1
|
|
||||||
"""
|
|
||||||
|
|
||||||
_ROUTES_V6 = """default via 2001:db8::1 dev em1
|
|
||||||
2001:db8:dead:beef:cafe::/56 via fd00:fd00:2000::1 dev em1
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_OVS_INTERFACE = _BASE_IFCFG + """DEVICETYPE=ovs
|
|
||||||
TYPE=OVSPort
|
|
||||||
OVS_BRIDGE=br-ctlplane
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_OVS_BRIDGE_DHCP = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=br-ctlplane
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
DEVICETYPE=ovs
|
|
||||||
TYPE=OVSBridge
|
|
||||||
OVSBOOTPROTO=dhcp
|
|
||||||
OVSDHCPINTERFACES="em1"
|
|
||||||
"""
|
|
||||||
|
|
||||||
_LINUX_BRIDGE_DHCP = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=br-ctlplane
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
TYPE=Bridge
|
|
||||||
DELAY=0
|
|
||||||
BOOTPROTO=dhcp
|
|
||||||
"""
|
|
||||||
|
|
||||||
_OVS_BRIDGE_STATIC = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=br-ctlplane
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
DEVICETYPE=ovs
|
|
||||||
TYPE=OVSBridge
|
|
||||||
BOOTPROTO=static
|
|
||||||
IPADDR=192.168.1.2
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_LINUX_BRIDGE_STATIC = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=br-ctlplane
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
TYPE=Bridge
|
|
||||||
DELAY=0
|
|
||||||
BOOTPROTO=static
|
|
||||||
IPADDR=192.168.1.2
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE = _OVS_BRIDGE_DHCP + \
|
|
||||||
"OVS_EXTRA=\"set bridge br-ctlplane other-config:hwaddr=a1:b2:c3:d4:e5\"\n"
|
|
||||||
|
|
||||||
|
|
||||||
_OVS_BRIDGE_DHCP_OVS_EXTRA = _OVS_BRIDGE_DHCP + \
|
|
||||||
"OVS_EXTRA=\"set bridge br-ctlplane other-config:hwaddr=a1:b2:c3:d4:e5" + \
|
|
||||||
" -- br-set-external-id br-ctlplane bridge-id br-ctlplane\"\n"
|
|
||||||
|
|
||||||
|
|
||||||
_BASE_VLAN = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=vlan5
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
VLAN=yes
|
|
||||||
PHYSDEV=em1
|
|
||||||
"""
|
|
||||||
|
|
||||||
# vlans on an OVS bridge do not set VLAN=yes or PHYSDEV
|
|
||||||
_BASE_VLAN_OVS = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=vlan5
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
"""
|
|
||||||
|
|
||||||
_VLAN_NO_IP = _BASE_VLAN + "BOOTPROTO=none\n"
|
|
||||||
|
|
||||||
|
|
||||||
_VLAN_OVS = _BASE_VLAN_OVS + "DEVICETYPE=ovs\nBOOTPROTO=none\n"
|
|
||||||
|
|
||||||
|
|
||||||
_VLAN_OVS_BRIDGE = _BASE_VLAN_OVS + """DEVICETYPE=ovs
|
|
||||||
TYPE=OVSIntPort
|
|
||||||
OVS_BRIDGE=br-ctlplane
|
|
||||||
OVS_OPTIONS="tag=5"
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_VLAN_LINUX_BRIDGE = _BASE_VLAN_OVS + """VLAN=yes
|
|
||||||
PHYSDEV=em1
|
|
||||||
BRIDGE=br-ctlplane
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_OVS_BOND_DHCP = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=bond0
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
DEVICETYPE=ovs
|
|
||||||
TYPE=OVSBond
|
|
||||||
OVSBOOTPROTO=dhcp
|
|
||||||
BOND_IFACES="em1 em2"
|
|
||||||
"""
|
|
||||||
|
|
||||||
_LINUX_BOND_DHCP = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=bond0
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
BOOTPROTO=dhcp
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_LINUX_TEAM_DHCP = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=team0
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
BOOTPROTO=dhcp
|
|
||||||
DEVICETYPE=Team
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
_LINUX_BOND_INTERFACE = _BASE_IFCFG + """MASTER=bond0
|
|
||||||
SLAVE=yes
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_LINUX_TEAM_INTERFACE = _BASE_IFCFG + """TEAM_MASTER=team0
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_IVS_UPLINK = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
DEVICETYPE=ivs
|
|
||||||
IVS_BRIDGE=ivs
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_IVS_INTERFACE = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=storage5
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
TYPE=IVSIntPort
|
|
||||||
DEVICETYPE=ivs
|
|
||||||
IVS_BRIDGE=ivs
|
|
||||||
MTU=1500
|
|
||||||
BOOTPROTO=static
|
|
||||||
IPADDR=172.16.2.7
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_IVS_CONFIG = ('DAEMON_ARGS=\"--hitless --certificate /etc/ivs '
|
|
||||||
'--inband-vlan 4092 -u em1 --internal-port=storage5\"')
|
|
||||||
|
|
||||||
_NFVSWITCH_INTERFACE = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
DEVICETYPE=nfvswitch
|
|
||||||
NFVSWITCH_BRIDGE=nfvswitch
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
_NFVSWITCH_INTERNAL = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=storage5
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
TYPE=NFVSWITCHIntPort
|
|
||||||
DEVICETYPE=nfvswitch
|
|
||||||
NFVSWITCH_BRIDGE=nfvswitch
|
|
||||||
MTU=1500
|
|
||||||
BOOTPROTO=static
|
|
||||||
IPADDR=172.16.2.7
|
|
||||||
NETMASK=255.255.255.0
|
|
||||||
"""
|
|
||||||
|
|
||||||
_NFVSWITCH_CONFIG = ('SETUP_ARGS=\" -c 2,3,4,5 -u em1 -m storage5\"')
|
|
||||||
|
|
||||||
_OVS_IFCFG_PATCH_PORT = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=br-pub-patch
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
DEVICETYPE=ovs
|
|
||||||
TYPE=OVSPatchPort
|
|
||||||
OVS_BRIDGE=br-ex
|
|
||||||
OVS_PATCH_PEER=br-ex-patch
|
|
||||||
"""
|
|
||||||
|
|
||||||
_LINUX_TEAM_PRIMARY_IFACE = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
TEAM_MASTER=team1
|
|
||||||
TEAM_PORT_CONFIG='{"prio": 100}'
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
class TestIfcfgNetConfig(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestIfcfgNetConfig, self).setUp()
|
|
||||||
|
|
||||||
self.provider = impl_ifcfg.IfcfgNetConfig()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
super(TestIfcfgNetConfig, self).tearDown()
|
|
||||||
|
|
||||||
def get_interface_config(self, name='em1'):
|
|
||||||
return self.provider.interface_data[name]
|
|
||||||
|
|
||||||
def get_vlan_config(self, name='vlan1'):
|
|
||||||
return self.provider.vlan_data[name]
|
|
||||||
|
|
||||||
def get_linux_bond_config(self, name='bond0'):
|
|
||||||
return self.provider.linuxbond_data[name]
|
|
||||||
|
|
||||||
def get_linux_team_config(self, name='team0'):
|
|
||||||
return self.provider.linuxteam_data[name]
|
|
||||||
|
|
||||||
def get_route_config(self, name='em1'):
|
|
||||||
return self.provider.route_data.get(name, '')
|
|
||||||
|
|
||||||
def get_route6_config(self, name='em1'):
|
|
||||||
return self.provider.route6_data.get(name, '')
|
|
||||||
|
|
||||||
def test_add_base_interface(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_NO_IP, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_base_interface_vlan(self):
|
|
||||||
interface = objects.Interface('em1.120')
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_IFCFG_VLAN, self.get_interface_config('em1.120'))
|
|
||||||
|
|
||||||
def test_add_ovs_interface(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
interface.ovs_port = True
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_IFCFG, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_ovs_tunnel(self):
|
|
||||||
interface = objects.OvsTunnel('tun0')
|
|
||||||
interface.type = 'ovs_tunnel'
|
|
||||||
interface.tunnel_type = 'gre'
|
|
||||||
interface.ovs_options = ['options:remote_ip=192.168.1.1']
|
|
||||||
interface.bridge_name = 'br-ctlplane'
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_IFCFG_TUNNEL, self.get_interface_config('tun0'))
|
|
||||||
|
|
||||||
def test_add_ovs_patch_port(self):
|
|
||||||
patch_port = objects.OvsPatchPort("br-pub-patch")
|
|
||||||
patch_port.type = 'ovs_patch_port'
|
|
||||||
patch_port.bridge_name = 'br-ex'
|
|
||||||
patch_port.peer = 'br-ex-patch'
|
|
||||||
self.provider.add_interface(patch_port)
|
|
||||||
self.assertEqual(_OVS_IFCFG_PATCH_PORT,
|
|
||||||
self.get_interface_config('br-pub-patch'))
|
|
||||||
|
|
||||||
def test_add_interface_with_v4(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('em1', addresses=[v4_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFCFG, self.get_interface_config())
|
|
||||||
self.assertEqual('', self.get_route_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_v4_multiple(self):
|
|
||||||
addresses = [objects.Address('192.168.1.2/24'),
|
|
||||||
objects.Address('192.168.1.3/32'),
|
|
||||||
objects.Address('10.0.0.2/8')]
|
|
||||||
interface = objects.Interface('em1', addresses=addresses)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFCFG_MULTIPLE, self.get_interface_config())
|
|
||||||
self.assertEqual('', self.get_route_config())
|
|
||||||
|
|
||||||
def test_add_interface_map_persisted(self):
|
|
||||||
def test_interface_mac(name):
|
|
||||||
macs = {'em1': 'a1:b2:c3:d4:e5'}
|
|
||||||
return macs[name]
|
|
||||||
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
|
|
||||||
|
|
||||||
nic_mapping = {'nic1': 'em1'}
|
|
||||||
self.stubbed_numbered_nics = nic_mapping
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('nic1', addresses=[v4_addr],
|
|
||||||
nic_mapping=nic_mapping,
|
|
||||||
persist_mapping=True)
|
|
||||||
self.assertEqual('a1:b2:c3:d4:e5', interface.hwaddr)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFCFG_MAPPED, self.get_interface_config('nic1'))
|
|
||||||
self.assertEqual('', self.get_route_config('nic1'))
|
|
||||||
|
|
||||||
def test_add_interface_with_v6(self):
|
|
||||||
v6_addr = objects.Address('2001:abc:a::/64')
|
|
||||||
interface = objects.Interface('em1', addresses=[v6_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V6_IFCFG, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_add_interface_with_v6_multiple(self):
|
|
||||||
addresses = [objects.Address('2001:abc:a::/64'),
|
|
||||||
objects.Address('2001:abc:b::1/64'),
|
|
||||||
objects.Address('2001:abc:c::2/96')]
|
|
||||||
interface = objects.Interface('em1', addresses=addresses)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V6_IFCFG_MULTIPLE, self.get_interface_config())
|
|
||||||
|
|
||||||
def test_network_with_routes(self):
|
|
||||||
route1 = objects.Route('192.168.1.1', default=True)
|
|
||||||
route2 = objects.Route('192.168.1.1', '172.19.0.0/24')
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('em1', addresses=[v4_addr],
|
|
||||||
routes=[route1, route2])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_IFCFG, self.get_interface_config())
|
|
||||||
self.assertEqual(_ROUTES, self.get_route_config())
|
|
||||||
|
|
||||||
def test_network_with_ipv6_routes(self):
|
|
||||||
route1 = objects.Route('192.168.1.1', default=True)
|
|
||||||
route2 = objects.Route('192.168.1.1', '172.19.0.0/24')
|
|
||||||
route3 = objects.Route('2001:db8::1', default=True)
|
|
||||||
route4 = objects.Route('fd00:fd00:2000::1',
|
|
||||||
'2001:db8:dead:beef:cafe::/56')
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
v6_addr = objects.Address('2001:abc:a::/64')
|
|
||||||
interface = objects.Interface('em1', addresses=[v4_addr, v6_addr],
|
|
||||||
routes=[route1, route2, route3, route4])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_V4_V6_IFCFG, self.get_interface_config())
|
|
||||||
self.assertEqual(_ROUTES_V6, self.get_route6_config())
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_dhcp(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP,
|
|
||||||
self.provider.bridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_linux_bridge_with_dhcp(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.LinuxBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_linux_bridge(bridge)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_LINUX_BRIDGE_IFCFG, self.get_interface_config())
|
|
||||||
self.assertEqual(_LINUX_BRIDGE_DHCP,
|
|
||||||
self.provider.linuxbridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_static(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', members=[interface],
|
|
||||||
addresses=[v4_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_STATIC,
|
|
||||||
self.provider.bridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_tunnel(self):
|
|
||||||
interface = objects.OvsTunnel('tun0')
|
|
||||||
interface.type = 'ovs_tunnel'
|
|
||||||
interface.tunnel_type = 'gre'
|
|
||||||
interface.ovs_options = ['options:remote_ip=192.168.1.1']
|
|
||||||
interface.bridge_name = 'br-ctlplane'
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', members=[interface],
|
|
||||||
addresses=[v4_addr])
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.assertEqual(_OVS_IFCFG_TUNNEL, self.get_interface_config('tun0'))
|
|
||||||
self.assertEqual(_OVS_BRIDGE_STATIC,
|
|
||||||
self.provider.bridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_linux_bridge_static(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.LinuxBridge('br-ctlplane', members=[interface],
|
|
||||||
addresses=[v4_addr])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_LINUX_BRIDGE_IFCFG, self.get_interface_config())
|
|
||||||
self.assertEqual(_LINUX_BRIDGE_STATIC,
|
|
||||||
self.provider.bridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_dhcp_primary_interface(self):
|
|
||||||
def test_interface_mac(name):
|
|
||||||
return "a1:b2:c3:d4:e5"
|
|
||||||
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
|
|
||||||
|
|
||||||
interface = objects.Interface('em1', primary=True)
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP_PRIMARY_INTERFACE,
|
|
||||||
self.provider.bridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_ovs_bridge_with_dhcp_primary_interface_with_extra(self):
|
|
||||||
def test_interface_mac(name):
|
|
||||||
return "a1:b2:c3:d4:e5"
|
|
||||||
self.stubs.Set(utils, 'interface_mac', test_interface_mac)
|
|
||||||
|
|
||||||
interface = objects.Interface('em1', primary=True)
|
|
||||||
ovs_extra = "br-set-external-id br-ctlplane bridge-id br-ctlplane"
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface],
|
|
||||||
ovs_extra=[ovs_extra])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_OVS_INTERFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP_OVS_EXTRA,
|
|
||||||
self.provider.bridge_data['br-ctlplane'])
|
|
||||||
|
|
||||||
def test_network_ivs_with_uplink_and_interface(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
v4_addr = objects.Address('172.16.2.7/24')
|
|
||||||
ivs_interface = objects.IvsInterface(vlan_id=5,
|
|
||||||
name='storage',
|
|
||||||
addresses=[v4_addr])
|
|
||||||
bridge = objects.IvsBridge(members=[interface, ivs_interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_ivs_interface(ivs_interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_IVS_UPLINK, self.get_interface_config())
|
|
||||||
self.assertEqual(_IVS_INTERFACE,
|
|
||||||
self.provider.ivsinterface_data[ivs_interface.name])
|
|
||||||
data = self.provider.generate_ivs_config(['em1'], ['storage5'])
|
|
||||||
self.assertEqual(_IVS_CONFIG, data)
|
|
||||||
|
|
||||||
def test_network_nfvswitch_with_interfaces_and_internal_interfaces(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
v4_addr = objects.Address('172.16.2.7/24')
|
|
||||||
nfvswitch_internal = objects.NfvswitchInternal(vlan_id=5,
|
|
||||||
name='storage',
|
|
||||||
addresses=[v4_addr])
|
|
||||||
iface_name = nfvswitch_internal.name
|
|
||||||
bridge = objects.NfvswitchBridge(members=[interface,
|
|
||||||
nfvswitch_internal],
|
|
||||||
cpus="2,3,4,5")
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_nfvswitch_internal(nfvswitch_internal)
|
|
||||||
self.provider.add_nfvswitch_bridge(bridge)
|
|
||||||
self.assertEqual(_NFVSWITCH_INTERFACE, self.get_interface_config())
|
|
||||||
self.assertEqual(_NFVSWITCH_INTERNAL,
|
|
||||||
self.provider.nfvswitch_intiface_data[iface_name])
|
|
||||||
data = self.provider.generate_nfvswitch_config(['em1'], ['storage5'])
|
|
||||||
self.assertEqual(_NFVSWITCH_CONFIG, data)
|
|
||||||
|
|
||||||
def test_add_ib_interface_with_v4_multiple(self):
|
|
||||||
addresses = [objects.Address('192.168.1.2/24'),
|
|
||||||
objects.Address('192.168.1.3/32'),
|
|
||||||
objects.Address('10.0.0.2/8')]
|
|
||||||
ib_interface = objects.IbInterface('ib0', addresses=addresses)
|
|
||||||
self.provider.add_interface(ib_interface)
|
|
||||||
self.assertEqual(_IB_V4_IFCFG_MULTIPLE,
|
|
||||||
self.get_interface_config('ib0'))
|
|
||||||
self.assertEqual('', self.get_route_config())
|
|
||||||
|
|
||||||
def test_add_vlan(self):
|
|
||||||
vlan = objects.Vlan('em1', 5)
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.assertEqual(_VLAN_NO_IP, self.get_vlan_config('vlan5'))
|
|
||||||
|
|
||||||
def test_add_vlan_ovs(self):
|
|
||||||
vlan = objects.Vlan('em1', 5)
|
|
||||||
vlan.ovs_port = True
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.assertEqual(_VLAN_OVS, self.get_vlan_config('vlan5'))
|
|
||||||
|
|
||||||
def test_add_vlan_mtu_1500(self):
|
|
||||||
vlan = objects.Vlan('em1', 5, mtu=1500)
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
expected = _VLAN_NO_IP + 'MTU=1500\n'
|
|
||||||
self.assertEqual(expected, self.get_vlan_config('vlan5'))
|
|
||||||
|
|
||||||
def test_add_ovs_bridge_with_vlan(self):
|
|
||||||
vlan = objects.Vlan('em1', 5)
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[vlan])
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_VLAN_OVS_BRIDGE, self.get_vlan_config('vlan5'))
|
|
||||||
|
|
||||||
def test_add_linux_bridge_with_vlan(self):
|
|
||||||
vlan = objects.Vlan('em1', 5)
|
|
||||||
bridge = objects.LinuxBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[vlan])
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.assertEqual(_VLAN_LINUX_BRIDGE, self.get_vlan_config('vlan5'))
|
|
||||||
|
|
||||||
def test_ovs_bond(self):
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2')
|
|
||||||
bond = objects.OvsBond('bond0', use_dhcp=True,
|
|
||||||
members=[interface1, interface2])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.provider.add_bond(bond)
|
|
||||||
self.assertEqual(_NO_IP, self.get_interface_config('em1'))
|
|
||||||
|
|
||||||
em2_config = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em2
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
self.assertEqual(em2_config, self.get_interface_config('em2'))
|
|
||||||
self.assertEqual(_OVS_BOND_DHCP,
|
|
||||||
self.get_interface_config('bond0'))
|
|
||||||
|
|
||||||
def test_linux_bond(self):
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2')
|
|
||||||
bond = objects.LinuxBond('bond0', use_dhcp=True,
|
|
||||||
members=[interface1, interface2])
|
|
||||||
self.provider.add_linux_bond(bond)
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.assertEqual(_LINUX_BOND_DHCP,
|
|
||||||
self.get_linux_bond_config('bond0'))
|
|
||||||
self.assertEqual(_LINUX_BOND_INTERFACE,
|
|
||||||
self.get_interface_config('em1'))
|
|
||||||
|
|
||||||
def test_linux_team(self):
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2')
|
|
||||||
team = objects.LinuxTeam('team0', use_dhcp=True,
|
|
||||||
members=[interface1, interface2])
|
|
||||||
self.provider.add_linux_team(team)
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.assertEqual(_LINUX_TEAM_DHCP,
|
|
||||||
self.get_linux_team_config('team0'))
|
|
||||||
self.assertEqual(_LINUX_TEAM_INTERFACE,
|
|
||||||
self.get_interface_config('em1'))
|
|
||||||
|
|
||||||
def test_interface_defroute(self):
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2', defroute=False)
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
em1_config = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
BOOTPROTO=none
|
|
||||||
"""
|
|
||||||
em2_config = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em2
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
BOOTPROTO=none
|
|
||||||
DEFROUTE=no
|
|
||||||
"""
|
|
||||||
self.assertEqual(em1_config, self.get_interface_config('em1'))
|
|
||||||
self.assertEqual(em2_config, self.get_interface_config('em2'))
|
|
||||||
|
|
||||||
def test_interface_dhclient_opts(self):
|
|
||||||
interface1 = objects.Interface('em1', dhclient_args='--foobar')
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
em1_config = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
PEERDNS=no
|
|
||||||
BOOTPROTO=none
|
|
||||||
DHCLIENTARGS=--foobar
|
|
||||||
"""
|
|
||||||
self.assertEqual(em1_config, self.get_interface_config('em1'))
|
|
||||||
|
|
||||||
def test_interface_single_dns_server(self):
|
|
||||||
interface1 = objects.Interface('em1', dns_servers=['1.2.3.4'])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
em1_config = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
BOOTPROTO=none
|
|
||||||
DNS1=1.2.3.4
|
|
||||||
"""
|
|
||||||
self.assertEqual(em1_config, self.get_interface_config('em1'))
|
|
||||||
|
|
||||||
def test_interface_dns_servers(self):
|
|
||||||
interface1 = objects.Interface('em1', dns_servers=['1.2.3.4',
|
|
||||||
'5.6.7.8'])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
em1_config = """# This file is autogenerated by os-net-config
|
|
||||||
DEVICE=em1
|
|
||||||
ONBOOT=yes
|
|
||||||
HOTPLUG=no
|
|
||||||
NM_CONTROLLED=no
|
|
||||||
BOOTPROTO=none
|
|
||||||
DNS1=1.2.3.4
|
|
||||||
DNS2=5.6.7.8
|
|
||||||
"""
|
|
||||||
self.assertEqual(em1_config, self.get_interface_config('em1'))
|
|
||||||
|
|
||||||
|
|
||||||
class TestIfcfgNetConfigApply(base.TestCase):
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(TestIfcfgNetConfigApply, self).setUp()
|
|
||||||
self.temp_ifcfg_file = tempfile.NamedTemporaryFile()
|
|
||||||
self.temp_route_file = tempfile.NamedTemporaryFile()
|
|
||||||
self.temp_route6_file = tempfile.NamedTemporaryFile()
|
|
||||||
self.temp_bridge_file = tempfile.NamedTemporaryFile()
|
|
||||||
self.temp_cleanup_file = tempfile.NamedTemporaryFile(delete=False)
|
|
||||||
self.ifup_interface_names = []
|
|
||||||
self.ovs_appctl_cmds = []
|
|
||||||
|
|
||||||
def test_ifcfg_path(name):
|
|
||||||
return self.temp_ifcfg_file.name
|
|
||||||
self.stubs.Set(impl_ifcfg, 'ifcfg_config_path', test_ifcfg_path)
|
|
||||||
|
|
||||||
def test_routes_path(name):
|
|
||||||
return self.temp_route_file.name
|
|
||||||
self.stubs.Set(impl_ifcfg, 'route_config_path', test_routes_path)
|
|
||||||
|
|
||||||
def test_routes6_path(name):
|
|
||||||
return self.temp_route6_file.name
|
|
||||||
self.stubs.Set(impl_ifcfg, 'route6_config_path', test_routes6_path)
|
|
||||||
|
|
||||||
def test_bridge_path(name):
|
|
||||||
return self.temp_bridge_file.name
|
|
||||||
self.stubs.Set(impl_ifcfg, 'bridge_config_path', test_bridge_path)
|
|
||||||
|
|
||||||
def test_cleanup_pattern():
|
|
||||||
return self.temp_cleanup_file.name
|
|
||||||
self.stubs.Set(impl_ifcfg, 'cleanup_pattern', test_cleanup_pattern)
|
|
||||||
|
|
||||||
def test_execute(*args, **kwargs):
|
|
||||||
if args[0] == '/sbin/ifup':
|
|
||||||
self.ifup_interface_names.append(args[1])
|
|
||||||
elif args[0] == '/bin/ovs-appctl':
|
|
||||||
self.ovs_appctl_cmds.append(' '.join(args))
|
|
||||||
pass
|
|
||||||
self.stubs.Set(processutils, 'execute', test_execute)
|
|
||||||
|
|
||||||
self.provider = impl_ifcfg.IfcfgNetConfig()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.temp_ifcfg_file.close()
|
|
||||||
self.temp_route_file.close()
|
|
||||||
self.temp_route6_file.close()
|
|
||||||
self.temp_bridge_file.close()
|
|
||||||
if os.path.exists(self.temp_cleanup_file.name):
|
|
||||||
self.temp_cleanup_file.close()
|
|
||||||
super(TestIfcfgNetConfigApply, self).tearDown()
|
|
||||||
|
|
||||||
def test_network_apply(self):
|
|
||||||
route1 = objects.Route('192.168.1.1', default=True)
|
|
||||||
route2 = objects.Route('192.168.1.1', '172.19.0.0/24')
|
|
||||||
v4_addr = objects.Address('192.168.1.2/24')
|
|
||||||
interface = objects.Interface('em1', addresses=[v4_addr],
|
|
||||||
routes=[route1, route2])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
|
|
||||||
self.provider.apply()
|
|
||||||
|
|
||||||
ifcfg_data = utils.get_file_data(self.temp_ifcfg_file.name)
|
|
||||||
self.assertEqual(_V4_IFCFG, ifcfg_data)
|
|
||||||
route_data = utils.get_file_data(self.temp_route_file.name)
|
|
||||||
self.assertEqual(_ROUTES, route_data)
|
|
||||||
|
|
||||||
def test_dhcp_ovs_bridge_network_apply(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
|
|
||||||
ifcfg_data = utils.get_file_data(self.temp_ifcfg_file.name)
|
|
||||||
self.assertEqual(_OVS_INTERFACE, ifcfg_data)
|
|
||||||
bridge_data = utils.get_file_data(self.temp_bridge_file.name)
|
|
||||||
self.assertEqual(_OVS_BRIDGE_DHCP, bridge_data)
|
|
||||||
route_data = utils.get_file_data(self.temp_route_file.name)
|
|
||||||
self.assertEqual("", route_data)
|
|
||||||
|
|
||||||
def test_apply_noactivate(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply(activate=False)
|
|
||||||
self.assertEqual([], self.ifup_interface_names)
|
|
||||||
|
|
||||||
def test_bond_active_slave(self):
|
|
||||||
# setup and apply a bond
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2', primary=True)
|
|
||||||
bond = objects.OvsBond('bond1', use_dhcp=True,
|
|
||||||
members=[interface1, interface2])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.provider.add_bond(bond)
|
|
||||||
self.provider.apply()
|
|
||||||
ovs_appctl_cmds = '/bin/ovs-appctl bond/set-active-slave bond1 em2'
|
|
||||||
self.assertIn(ovs_appctl_cmds, self.ovs_appctl_cmds)
|
|
||||||
|
|
||||||
def test_bond_active_ordering(self):
|
|
||||||
# setup and apply a bond
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2')
|
|
||||||
bond = objects.OvsBond('bond1', use_dhcp=True,
|
|
||||||
members=[interface1, interface2])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.provider.add_bond(bond)
|
|
||||||
self.provider.apply()
|
|
||||||
ovs_appctl_cmds = '/bin/ovs-appctl bond/set-active-slave bond1 em1'
|
|
||||||
self.assertIn(ovs_appctl_cmds, self.ovs_appctl_cmds)
|
|
||||||
|
|
||||||
def test_restart_children_on_change(self):
|
|
||||||
# setup and apply a bridge
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
self.assertIn('em1', self.ifup_interface_names)
|
|
||||||
self.assertIn('br-ctlplane', self.ifup_interface_names)
|
|
||||||
|
|
||||||
# changing the bridge should restart the interface too
|
|
||||||
self.ifup_interface_names = []
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=False,
|
|
||||||
members=[interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
self.assertIn('em1', self.ifup_interface_names)
|
|
||||||
|
|
||||||
# test infiniband interfaces act as proper bridge members
|
|
||||||
ib_interface = objects.IbInterface('ib0')
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[ib_interface])
|
|
||||||
self.provider.add_interface(ib_interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
self.assertIn('ib0', self.ifup_interface_names)
|
|
||||||
self.assertIn('br-ctlplane', self.ifup_interface_names)
|
|
||||||
|
|
||||||
# changing the bridge should restart the interface too
|
|
||||||
self.ifup_interface_names = []
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=False,
|
|
||||||
members=[ib_interface])
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
self.assertIn('ib0', self.ifup_interface_names)
|
|
||||||
|
|
||||||
# setup and apply a bond on a bridge
|
|
||||||
self.ifup_interface_names = []
|
|
||||||
interface1 = objects.Interface('em1')
|
|
||||||
interface2 = objects.Interface('em2')
|
|
||||||
bond = objects.OvsBond('bond0',
|
|
||||||
members=[interface1, interface2])
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=True,
|
|
||||||
members=[bond])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.provider.add_bond(bond)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
|
|
||||||
# changing the bridge should restart everything
|
|
||||||
self.ifup_interface_names = []
|
|
||||||
bridge = objects.OvsBridge('br-ctlplane', use_dhcp=False,
|
|
||||||
members=[bond])
|
|
||||||
self.provider.add_interface(interface1)
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.provider.add_bond(bond)
|
|
||||||
self.provider.add_bridge(bridge)
|
|
||||||
self.provider.apply()
|
|
||||||
self.assertIn('br-ctlplane', self.ifup_interface_names)
|
|
||||||
self.assertIn('bond0', self.ifup_interface_names)
|
|
||||||
self.assertIn('em1', self.ifup_interface_names)
|
|
||||||
self.assertIn('em2', self.ifup_interface_names)
|
|
||||||
|
|
||||||
def test_restart_interface_counts(self):
|
|
||||||
interface = objects.Interface('em1')
|
|
||||||
self.provider.add_interface(interface)
|
|
||||||
interface2 = objects.Interface('em2')
|
|
||||||
self.provider.add_interface(interface2)
|
|
||||||
self.provider.apply()
|
|
||||||
self.assertEqual(1, self.ifup_interface_names.count("em1"))
|
|
||||||
self.assertEqual(1, self.ifup_interface_names.count("em2"))
|
|
||||||
|
|
||||||
def test_vlan_apply(self):
|
|
||||||
vlan = objects.Vlan('em1', 5)
|
|
||||||
self.provider.add_vlan(vlan)
|
|
||||||
self.provider.apply()
|
|
||||||
|
|
||||||
ifcfg_data = utils.get_file_data(self.temp_ifcfg_file.name)
|
|
||||||
self.assertEqual(_VLAN_NO_IP, ifcfg_data)
|
|
||||||
|
|
||||||
def test_cleanup(self):
|
|
||||||
self.provider.apply(cleanup=True)
|
|
||||||
self.assertTrue(not os.path.exists(self.temp_cleanup_file.name))
|
|
||||||
|
|
||||||
def test_cleanup_not_loopback(self):
|
|
||||||
tmp_lo_file = '%s-lo' % self.temp_cleanup_file.name
|
|
||||||
utils.write_config(tmp_lo_file, 'foo')
|
|
||||||
|
|
||||||
def test_cleanup_pattern():
|
|
||||||
return '%s-*' % self.temp_cleanup_file.name
|
|
||||||
self.stubs.Set(impl_ifcfg, 'cleanup_pattern', test_cleanup_pattern)
|
|
||||||
|
|
||||||
self.provider.apply(cleanup=True)
|
|
||||||
self.assertTrue(os.path.exists(tmp_lo_file))
|
|
||||||
os.remove(tmp_lo_file)
|
|
@ -1,819 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import json
|
|
||||||
import six
|
|
||||||
|
|
||||||
from os_net_config import objects
|
|
||||||
from os_net_config.tests import base
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
class TestRoute(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json(self):
|
|
||||||
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24"}'
|
|
||||||
route = objects.Route.from_json(json.loads(data))
|
|
||||||
self.assertEqual("172.19.0.1", route.next_hop)
|
|
||||||
self.assertEqual("172.19.0.0/24", route.ip_netmask)
|
|
||||||
self.assertFalse(route.default)
|
|
||||||
|
|
||||||
def test_from_json_default_route(self):
|
|
||||||
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24", ' \
|
|
||||||
'"default": true}'
|
|
||||||
route = objects.Route.from_json(json.loads(data))
|
|
||||||
self.assertEqual("172.19.0.1", route.next_hop)
|
|
||||||
self.assertEqual("172.19.0.0/24", route.ip_netmask)
|
|
||||||
self.assertTrue(route.default)
|
|
||||||
|
|
||||||
data = '{"next_hop": "172.19.0.1", "ip_netmask": "172.19.0.0/24", ' \
|
|
||||||
'"default": "true"}'
|
|
||||||
route = objects.Route.from_json(json.loads(data))
|
|
||||||
self.assertEqual("172.19.0.1", route.next_hop)
|
|
||||||
self.assertEqual("172.19.0.0/24", route.ip_netmask)
|
|
||||||
self.assertTrue(route.default)
|
|
||||||
|
|
||||||
|
|
||||||
class TestAddress(base.TestCase):
|
|
||||||
|
|
||||||
def test_ipv4_address(self):
|
|
||||||
address = objects.Address('192.168.1.1/24')
|
|
||||||
self.assertEqual("192.168.1.1", address.ip)
|
|
||||||
self.assertEqual("255.255.255.0", address.netmask)
|
|
||||||
self.assertEqual(4, address.version)
|
|
||||||
|
|
||||||
def test_ipv6_address(self):
|
|
||||||
address = objects.Address('2001:abc:a::/64')
|
|
||||||
self.assertEqual("2001:abc:a::", address.ip)
|
|
||||||
self.assertEqual("ffff:ffff:ffff:ffff::", address.netmask)
|
|
||||||
self.assertEqual(6, address.version)
|
|
||||||
|
|
||||||
def test_from_json(self):
|
|
||||||
data = '{"ip_netmask": "192.0.2.5/24"}'
|
|
||||||
address = objects.Address.from_json(json.loads(data))
|
|
||||||
self.assertEqual("192.0.2.5", address.ip)
|
|
||||||
self.assertEqual("255.255.255.0", address.netmask)
|
|
||||||
self.assertEqual(4, address.version)
|
|
||||||
|
|
||||||
def test_from_json_invalid(self):
|
|
||||||
self.assertRaises(objects.InvalidConfigException,
|
|
||||||
objects.Address.from_json,
|
|
||||||
{})
|
|
||||||
data = '{"ip_netmask": false}'
|
|
||||||
json_data = json.loads(data)
|
|
||||||
self.assertRaises(objects.InvalidConfigException,
|
|
||||||
objects.Address.from_json,
|
|
||||||
json_data)
|
|
||||||
|
|
||||||
|
|
||||||
class TestInterface(base.TestCase):
|
|
||||||
|
|
||||||
def test_interface_addresses(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.1/24')
|
|
||||||
v6_addr = objects.Address('2001:abc:a::/64')
|
|
||||||
interface = objects.Interface('foo', addresses=[v4_addr, v6_addr])
|
|
||||||
self.assertEqual("192.168.1.1", interface.v4_addresses()[0].ip)
|
|
||||||
self.assertEqual("2001:abc:a::", interface.v6_addresses()[0].ip)
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = '{"type": "interface", "name": "em1", "use_dhcp": true}'
|
|
||||||
interface = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("em1", interface.name)
|
|
||||||
self.assertTrue(interface.use_dhcp)
|
|
||||||
|
|
||||||
def test_from_json_defroute(self):
|
|
||||||
data = '{"type": "interface", "name": "em1", "use_dhcp": true}'
|
|
||||||
interface1 = objects.object_from_json(json.loads(data))
|
|
||||||
data = """{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"defroute": false
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
interface2 = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertTrue(interface1.defroute)
|
|
||||||
self.assertFalse(interface2.defroute)
|
|
||||||
|
|
||||||
def test_from_json_dhclient_args(self):
|
|
||||||
data = """{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"dhclient_args": "--foobar"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
interface1 = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("--foobar", interface1.dhclient_args)
|
|
||||||
|
|
||||||
def test_from_json_dns_servers(self):
|
|
||||||
data = """{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"dns_servers": ["1.2.3.4"]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
interface1 = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual(["1.2.3.4"], interface1.dns_servers)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_nic1(self):
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "em3"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = '{"type": "interface", "name": "nic1", "use_dhcp": true}'
|
|
||||||
interface = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("em3", interface.name)
|
|
||||||
self.assertTrue(interface.use_dhcp)
|
|
||||||
|
|
||||||
def test_from_json_with_addresses(self):
|
|
||||||
data = """{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"use_dhcp": false,
|
|
||||||
"mtu": 1501,
|
|
||||||
"addresses": [{
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}],
|
|
||||||
"routes": [{
|
|
||||||
"next_hop": "192.0.2.1",
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
interface = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("em1", interface.name)
|
|
||||||
self.assertFalse(interface.use_dhcp)
|
|
||||||
self.assertFalse(interface.use_dhcpv6)
|
|
||||||
self.assertEqual(1501, interface.mtu)
|
|
||||||
address1 = interface.v4_addresses()[0]
|
|
||||||
self.assertEqual("192.0.2.1", address1.ip)
|
|
||||||
self.assertEqual("255.255.255.0", address1.netmask)
|
|
||||||
route1 = interface.routes[0]
|
|
||||||
self.assertEqual("192.0.2.1", route1.next_hop)
|
|
||||||
self.assertEqual("192.0.2.1/24", route1.ip_netmask)
|
|
||||||
|
|
||||||
|
|
||||||
class TestVlan(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = '{"type": "vlan", "device": "em1", "vlan_id": 16,' \
|
|
||||||
'"use_dhcp": true}'
|
|
||||||
vlan = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("em1", vlan.device)
|
|
||||||
self.assertEqual(16, vlan.vlan_id)
|
|
||||||
self.assertTrue(vlan.use_dhcp)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_nic1(self):
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "em4"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = '{"type": "vlan", "device": "nic1", "vlan_id": 16,' \
|
|
||||||
'"use_dhcp": true}'
|
|
||||||
vlan = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("em4", vlan.device)
|
|
||||||
self.assertEqual(16, vlan.vlan_id)
|
|
||||||
self.assertTrue(vlan.use_dhcp)
|
|
||||||
|
|
||||||
|
|
||||||
class TestBridge(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
self.assertTrue(interface1.ovs_port)
|
|
||||||
self.assertEqual("br-foo", interface1.bridge_name)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_with_nic1(self):
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "em5"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic1"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em5", interface1.name)
|
|
||||||
self.assertTrue(interface1.ovs_port)
|
|
||||||
self.assertEqual("br-foo", interface1.bridge_name)
|
|
||||||
|
|
||||||
def test_from_json_primary_interface(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"primary": "true"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
self.assertEqual("em1", bridge.primary_interface_name)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
self.assertTrue(interface1.ovs_port)
|
|
||||||
self.assertTrue(interface1.primary)
|
|
||||||
self.assertEqual("br-foo", interface1.bridge_name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
self.assertTrue(interface2.ovs_port)
|
|
||||||
self.assertEqual("br-foo", interface2.bridge_name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestLinuxBridge(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = """{
|
|
||||||
"type": "linux_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
self.assertFalse(interface1.ovs_port)
|
|
||||||
self.assertEqual("br-foo", interface1.linux_bridge_name)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_with_nic1(self):
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "em5"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = """{
|
|
||||||
"type": "linux_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic1"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em5", interface1.name)
|
|
||||||
self.assertFalse(interface1.ovs_port)
|
|
||||||
self.assertEqual("br-foo", interface1.linux_bridge_name)
|
|
||||||
|
|
||||||
def test_from_json_primary_interface(self):
|
|
||||||
data = """{
|
|
||||||
"type": "linux_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1",
|
|
||||||
"primary": "true"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
self.assertEqual("em1", bridge.primary_interface_name)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
self.assertFalse(interface1.ovs_port)
|
|
||||||
self.assertTrue(interface1.primary)
|
|
||||||
self.assertEqual("br-foo", interface1.linux_bridge_name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
self.assertFalse(interface2.ovs_port)
|
|
||||||
self.assertEqual("br-foo", interface2.linux_bridge_name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestIvsBridge(base.TestCase):
|
|
||||||
|
|
||||||
def test_interface_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ivs_bridge",
|
|
||||||
"members": [{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic2"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("ivs", bridge.name)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("nic2", interface1.name)
|
|
||||||
self.assertEqual(False, interface1.ovs_port)
|
|
||||||
self.assertEqual("ivs", interface1.ivs_bridge_name)
|
|
||||||
|
|
||||||
def test_ivs_interface_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ivs_bridge",
|
|
||||||
"members": [{
|
|
||||||
"type": "ivs_interface",
|
|
||||||
"name": "storage",
|
|
||||||
"vlan_id": 202
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("ivs", bridge.name)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("storage202", interface1.name)
|
|
||||||
self.assertEqual(False, interface1.ovs_port)
|
|
||||||
self.assertEqual("ivs", interface1.ivs_bridge_name)
|
|
||||||
|
|
||||||
def test_bond_interface_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ivs_bridge",
|
|
||||||
"members": [{
|
|
||||||
"type": "linux_bond",
|
|
||||||
"name": "bond1",
|
|
||||||
"members": [
|
|
||||||
{"type": "interface", "name": "nic2"},
|
|
||||||
{"type": "interface", "name": "nic3"}
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
err = self.assertRaises(objects.InvalidConfigException,
|
|
||||||
objects.IvsBridge.from_json,
|
|
||||||
json.loads(data))
|
|
||||||
expected = 'IVS does not support bond interfaces.'
|
|
||||||
self.assertIn(expected, err)
|
|
||||||
|
|
||||||
|
|
||||||
class TestNfvswitchBridge(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "nfvswitch_bridge",
|
|
||||||
"cpus": "2,3,4,5",
|
|
||||||
"members": [
|
|
||||||
{"type": "interface", "name": "nic2"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("nfvswitch", bridge.name)
|
|
||||||
self.assertEqual("2,3,4,5", bridge.cpus)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("nic2", interface1.name)
|
|
||||||
self.assertEqual(False, interface1.ovs_port)
|
|
||||||
self.assertEqual("nfvswitch", interface1.nfvswitch_bridge_name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestNfvswitchInterface(base.TestCase):
|
|
||||||
|
|
||||||
def test_interface_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "nfvswitch_bridge",
|
|
||||||
"cpus": "2,3,4,5",
|
|
||||||
"members": [
|
|
||||||
{"type": "interface","name": "nic1"},
|
|
||||||
{"type": "interface","name": "nic2"}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("nfvswitch", bridge.name)
|
|
||||||
self.assertEqual("2,3,4,5", bridge.cpus)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("nic1", interface1.name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("nic2", interface2.name)
|
|
||||||
self.assertEqual(False, interface2.ovs_port)
|
|
||||||
self.assertEqual("nfvswitch", interface1.nfvswitch_bridge_name)
|
|
||||||
|
|
||||||
def test_nfvswitch_internal_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "nfvswitch_bridge",
|
|
||||||
"cpus": "2,3,4,5",
|
|
||||||
"members": [
|
|
||||||
{"type": "nfvswitch_internal", "name": "storage", "vlan_id": 202},
|
|
||||||
{"type": "nfvswitch_internal", "name": "api", "vlan_id": 201}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("nfvswitch", bridge.name)
|
|
||||||
self.assertEqual("2,3,4,5", bridge.cpus)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("storage202", interface1.name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("api201", interface2.name)
|
|
||||||
self.assertEqual(False, interface1.ovs_port)
|
|
||||||
self.assertEqual("nfvswitch", interface1.nfvswitch_bridge_name)
|
|
||||||
|
|
||||||
def test_bond_interface_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "nfvswitch_bridge",
|
|
||||||
"cpus": "2,3,4,5",
|
|
||||||
"members": [{
|
|
||||||
"type": "linux_bond", "name": "bond1", "members":
|
|
||||||
[{"type": "interface", "name": "nic2"},
|
|
||||||
{"type": "interface", "name": "nic3"}]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
err = self.assertRaises(objects.InvalidConfigException,
|
|
||||||
objects.NfvswitchBridge.from_json,
|
|
||||||
json.loads(data))
|
|
||||||
expected = 'NFVSwitch does not support bond interfaces.'
|
|
||||||
self.assertIn(expected, err)
|
|
||||||
|
|
||||||
|
|
||||||
class TestBond(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bond",
|
|
||||||
"name": "bond1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("bond1", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_with_nic1_nic2(self):
|
|
||||||
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "em1", "nic2": "em2"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bond",
|
|
||||||
"name": "bond1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("bond1", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestLinuxTeam(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = """{
|
|
||||||
"type": "team",
|
|
||||||
"name": "team1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{ "type": "interface", "name": "em1", "primary": true },
|
|
||||||
{ "type": "interface", "name": "em2" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
team = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("team1", team.name)
|
|
||||||
self.assertTrue(team.use_dhcp)
|
|
||||||
interface1 = team.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
interface2 = team.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestLinuxBond(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = """{
|
|
||||||
"type": "linux_bond",
|
|
||||||
"name": "bond1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "em2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("bond1", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_with_nic1_nic2(self):
|
|
||||||
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "em1", "nic2": "em2"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bond",
|
|
||||||
"name": "bond1",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"members": [
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic1"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "interface",
|
|
||||||
"name": "nic2"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("bond1", bridge.name)
|
|
||||||
self.assertTrue(bridge.use_dhcp)
|
|
||||||
interface1 = bridge.members[0]
|
|
||||||
self.assertEqual("em1", interface1.name)
|
|
||||||
interface2 = bridge.members[1]
|
|
||||||
self.assertEqual("em2", interface2.name)
|
|
||||||
|
|
||||||
|
|
||||||
class TestOvsTunnel(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_bridge",
|
|
||||||
"name": "br-foo",
|
|
||||||
"members": [{
|
|
||||||
"type": "ovs_tunnel",
|
|
||||||
"name": "tun0",
|
|
||||||
"tunnel_type": "gre",
|
|
||||||
"ovs_options": [
|
|
||||||
"remote_ip=192.168.1.1"
|
|
||||||
],
|
|
||||||
"ovs_extra": [
|
|
||||||
"ovs extra"
|
|
||||||
]
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
bridge = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-foo", bridge.name)
|
|
||||||
tun0 = bridge.members[0]
|
|
||||||
self.assertEqual("tun0", tun0.name)
|
|
||||||
self.assertFalse(tun0.ovs_port)
|
|
||||||
self.assertEqual("br-foo", tun0.bridge_name)
|
|
||||||
self.assertEqual("gre", tun0.tunnel_type)
|
|
||||||
self.assertEqual(
|
|
||||||
["options:remote_ip=192.168.1.1"],
|
|
||||||
tun0.ovs_options)
|
|
||||||
self.assertEqual(
|
|
||||||
["ovs extra"],
|
|
||||||
tun0.ovs_extra)
|
|
||||||
|
|
||||||
|
|
||||||
class TestOvsPatchPort(base.TestCase):
|
|
||||||
|
|
||||||
def test_from_json(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ovs_patch_port",
|
|
||||||
"name": "br-pub-patch",
|
|
||||||
"bridge_name": "br-ex",
|
|
||||||
"peer": "br-ex-patch"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
patch_port = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("br-pub-patch", patch_port.name)
|
|
||||||
self.assertEqual("br-ex", patch_port.bridge_name)
|
|
||||||
self.assertEqual("br-ex-patch", patch_port.peer)
|
|
||||||
|
|
||||||
|
|
||||||
class TestIbInterface(base.TestCase):
|
|
||||||
|
|
||||||
def test_ib_interface_addresses(self):
|
|
||||||
v4_addr = objects.Address('192.168.1.1/24')
|
|
||||||
v6_addr = objects.Address('2001:abc:a::/64')
|
|
||||||
ib_interface = objects.IbInterface('foo', addresses=[v4_addr, v6_addr])
|
|
||||||
self.assertEqual("192.168.1.1", ib_interface.v4_addresses()[0].ip)
|
|
||||||
self.assertEqual("2001:abc:a::", ib_interface.v6_addresses()[0].ip)
|
|
||||||
|
|
||||||
def test_from_json_dhcp(self):
|
|
||||||
data = '{"type": "ib_interface", "name": "ib0", "use_dhcp": true}'
|
|
||||||
ib_interface = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("ib0", ib_interface.name)
|
|
||||||
self.assertTrue(ib_interface.use_dhcp)
|
|
||||||
|
|
||||||
def test_from_json_defroute(self):
|
|
||||||
data = '{"type": "ib_interface", "name": "ib0", "use_dhcp": true}'
|
|
||||||
ib_interface1 = objects.object_from_json(json.loads(data))
|
|
||||||
data = """{
|
|
||||||
"type": "ib_interface",
|
|
||||||
"name": "ib0",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"defroute": false
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
ib_interface2 = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertTrue(ib_interface1.defroute)
|
|
||||||
self.assertFalse(ib_interface2.defroute)
|
|
||||||
|
|
||||||
def test_from_json_dhclient_args(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ib_interface",
|
|
||||||
"name": "ib0",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"dhclient_args": "--foobar"
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
ib_interface1 = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("--foobar", ib_interface1.dhclient_args)
|
|
||||||
|
|
||||||
def test_from_json_dns_servers(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ib_interface",
|
|
||||||
"name": "ib0",
|
|
||||||
"use_dhcp": true,
|
|
||||||
"dns_servers": ["1.2.3.4"]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
ib_interface1 = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual(["1.2.3.4"], ib_interface1.dns_servers)
|
|
||||||
|
|
||||||
def test_from_json_dhcp_nic1(self):
|
|
||||||
def dummy_numbered_nics(nic_mapping=None):
|
|
||||||
return {"nic1": "ib0"}
|
|
||||||
self.stubs.Set(objects, '_numbered_nics', dummy_numbered_nics)
|
|
||||||
|
|
||||||
data = '{"type": "ib_interface", "name": "nic1", "use_dhcp": true}'
|
|
||||||
ib_interface = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("ib0", ib_interface.name)
|
|
||||||
self.assertTrue(ib_interface.use_dhcp)
|
|
||||||
|
|
||||||
def test_from_json_with_addresses(self):
|
|
||||||
data = """{
|
|
||||||
"type": "ib_interface",
|
|
||||||
"name": "ib0",
|
|
||||||
"use_dhcp": false,
|
|
||||||
"mtu": 1501,
|
|
||||||
"addresses": [{
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}],
|
|
||||||
"routes": [{
|
|
||||||
"next_hop": "192.0.2.1",
|
|
||||||
"ip_netmask": "192.0.2.1/24"
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
ib_interface = objects.object_from_json(json.loads(data))
|
|
||||||
self.assertEqual("ib0", ib_interface.name)
|
|
||||||
self.assertFalse(ib_interface.use_dhcp)
|
|
||||||
self.assertFalse(ib_interface.use_dhcpv6)
|
|
||||||
self.assertEqual(1501, ib_interface.mtu)
|
|
||||||
address1 = ib_interface.v4_addresses()[0]
|
|
||||||
self.assertEqual("192.0.2.1", address1.ip)
|
|
||||||
self.assertEqual("255.255.255.0", address1.netmask)
|
|
||||||
route1 = ib_interface.routes[0]
|
|
||||||
self.assertEqual("192.0.2.1", route1.next_hop)
|
|
||||||
self.assertEqual("192.0.2.1/24", route1.ip_netmask)
|
|
||||||
|
|
||||||
|
|
||||||
class TestNumberedNicsMapping(base.TestCase):
|
|
||||||
|
|
||||||
# We want to test the function, not the dummy..
|
|
||||||
stub_numbered_nics = False
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
super(TestNumberedNicsMapping, self).tearDown()
|
|
||||||
objects._NUMBERED_NICS = None
|
|
||||||
|
|
||||||
def _stub_active_nics(self, nics):
|
|
||||||
def dummy_ordered_active_nics():
|
|
||||||
return nics
|
|
||||||
self.stubs.Set(utils, 'ordered_active_nics', dummy_ordered_active_nics)
|
|
||||||
|
|
||||||
def test_numbered_nics_default(self):
|
|
||||||
self._stub_active_nics(['em1', 'em2'])
|
|
||||||
expected = {'nic1': 'em1', 'nic2': 'em2'}
|
|
||||||
self.assertEqual(expected, objects._numbered_nics())
|
|
||||||
|
|
||||||
def test_numbered_nics_mapped(self):
|
|
||||||
self._stub_active_nics(['em1', 'em2'])
|
|
||||||
mapping = {'nic1': 'em2', 'nic2': 'em1'}
|
|
||||||
expected = {'nic1': 'em2', 'nic2': 'em1'}
|
|
||||||
self.assertEqual(expected, objects._numbered_nics(nic_mapping=mapping))
|
|
||||||
|
|
||||||
def test_numbered_nics_mapped_partial(self):
|
|
||||||
self._stub_active_nics(['em1', 'em2', 'em3', 'em4'])
|
|
||||||
mapping = {'nic1': 'em2', 'nic2': 'em1'}
|
|
||||||
expected = {'nic1': 'em2', 'nic2': 'em1', 'nic3': 'em3', 'nic4': 'em4'}
|
|
||||||
self.assertEqual(expected, objects._numbered_nics(nic_mapping=mapping))
|
|
||||||
|
|
||||||
def test_numbered_nics_map_error_notactive(self):
|
|
||||||
self._stub_active_nics(['em1', 'em2'])
|
|
||||||
mapping = {'nic1': 'em3', 'nic2': 'em1'}
|
|
||||||
expected = {'nic2': 'em1'}
|
|
||||||
self.assertEqual(expected, objects._numbered_nics(nic_mapping=mapping))
|
|
||||||
|
|
||||||
def test_numbered_nics_map_error_duplicate(self):
|
|
||||||
self._stub_active_nics(['em1', 'em2'])
|
|
||||||
mapping = {'nic1': 'em1', 'nic2': 'em1'}
|
|
||||||
err = self.assertRaises(objects.InvalidConfigException,
|
|
||||||
objects._numbered_nics, nic_mapping=mapping)
|
|
||||||
expected = 'em1 already mapped, check mapping file for duplicates'
|
|
||||||
self.assertIn(expected, six.text_type(err))
|
|
||||||
|
|
||||||
def test_numbered_nics_map_mac(self):
|
|
||||||
def dummy_interface_mac(name):
|
|
||||||
mac_map = {'em1': '12:34:56:78:9a:bc',
|
|
||||||
'em2': '12:34:56:de:f0:12'}
|
|
||||||
return mac_map[name]
|
|
||||||
self.stubs.Set(utils, 'interface_mac', dummy_interface_mac)
|
|
||||||
self._stub_active_nics(['em1', 'em2'])
|
|
||||||
mapping = {'nic1': '12:34:56:de:f0:12', 'nic2': '12:34:56:78:9a:bc'}
|
|
||||||
expected = {'nic1': 'em2', 'nic2': 'em1'}
|
|
||||||
self.assertEqual(expected, objects._numbered_nics(nic_mapping=mapping))
|
|
||||||
|
|
||||||
def test_numbered_nics_no_active(self):
|
|
||||||
self._stub_active_nics([])
|
|
||||||
expected = {}
|
|
||||||
# This only emits a warning, so it should still work
|
|
||||||
self.assertEqual(expected, objects._numbered_nics())
|
|
@ -1,30 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
"""
|
|
||||||
test_os_net_config
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
Tests for `os_net_config` module.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from os_net_config.tests import base
|
|
||||||
|
|
||||||
|
|
||||||
class TestOs_net_config(base.TestCase):
|
|
||||||
|
|
||||||
def test_something(self):
|
|
||||||
pass
|
|
@ -1,51 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import os.path
|
|
||||||
import shutil
|
|
||||||
import tempfile
|
|
||||||
|
|
||||||
from os_net_config.tests import base
|
|
||||||
from os_net_config import utils
|
|
||||||
|
|
||||||
|
|
||||||
class TestUtils(base.TestCase):
|
|
||||||
|
|
||||||
def test_ordered_active_nics(self):
|
|
||||||
|
|
||||||
tmpdir = tempfile.mkdtemp()
|
|
||||||
self.stubs.Set(utils, '_SYS_CLASS_NET', tmpdir)
|
|
||||||
|
|
||||||
def test_is_active_nic(interface_name):
|
|
||||||
return True
|
|
||||||
self.stubs.Set(utils, '_is_active_nic', test_is_active_nic)
|
|
||||||
|
|
||||||
for nic in ['a1', 'em1', 'em2', 'eth2', 'z1',
|
|
||||||
'enp8s0', 'enp10s0', 'enp1s0f0']:
|
|
||||||
with open(os.path.join(tmpdir, nic), 'w') as f:
|
|
||||||
f.write(nic)
|
|
||||||
|
|
||||||
nics = utils.ordered_active_nics()
|
|
||||||
self.assertEqual('em1', nics[0])
|
|
||||||
self.assertEqual('em2', nics[1])
|
|
||||||
self.assertEqual('eth2', nics[2])
|
|
||||||
self.assertEqual('a1', nics[3])
|
|
||||||
self.assertEqual('enp1s0f0', nics[4])
|
|
||||||
self.assertEqual('enp8s0', nics[5])
|
|
||||||
self.assertEqual('enp10s0', nics[6])
|
|
||||||
self.assertEqual('z1', nics[7])
|
|
||||||
|
|
||||||
shutil.rmtree(tmpdir)
|
|
@ -1,120 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import glob
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
_SYS_CLASS_NET = '/sys/class/net'
|
|
||||||
|
|
||||||
|
|
||||||
def write_config(filename, data):
|
|
||||||
with open(filename, 'w') as f:
|
|
||||||
f.write(str(data))
|
|
||||||
|
|
||||||
|
|
||||||
def get_file_data(filename):
|
|
||||||
if not os.path.exists(filename):
|
|
||||||
return ''
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open(filename, 'r') as f:
|
|
||||||
return f.read()
|
|
||||||
except IOError:
|
|
||||||
logger.error("Error reading file: %s" % filename)
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
|
||||||
def interface_mac(name):
|
|
||||||
try: # If the iface is part of a Linux bond, the real MAC is only here.
|
|
||||||
with open('/sys/class/net/%s/bonding_slave/perm_hwaddr' % name,
|
|
||||||
'r') as f:
|
|
||||||
return f.read().rstrip()
|
|
||||||
except IOError:
|
|
||||||
pass # Iface is not part of a bond, continue
|
|
||||||
|
|
||||||
try:
|
|
||||||
with open('/sys/class/net/%s/address' % name, 'r') as f:
|
|
||||||
return f.read().rstrip()
|
|
||||||
except IOError:
|
|
||||||
logger.error("Unable to read mac address: %s" % name)
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
def _is_active_nic(interface_name):
|
|
||||||
try:
|
|
||||||
if interface_name == 'lo':
|
|
||||||
return False
|
|
||||||
|
|
||||||
device_dir = _SYS_CLASS_NET + '/%s/device' % interface_name
|
|
||||||
has_device_dir = os.path.isdir(device_dir)
|
|
||||||
|
|
||||||
operstate = None
|
|
||||||
with open(_SYS_CLASS_NET + '/%s/operstate' % interface_name, 'r') as f:
|
|
||||||
operstate = f.read().rstrip().lower()
|
|
||||||
|
|
||||||
address = None
|
|
||||||
with open(_SYS_CLASS_NET + '/%s/address' % interface_name, 'r') as f:
|
|
||||||
address = f.read().rstrip()
|
|
||||||
|
|
||||||
if has_device_dir and operstate == 'up' and address:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except IOError:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
def _natural_sort_key(s):
|
|
||||||
nsre = re.compile('([0-9]+)')
|
|
||||||
return [int(text) if text.isdigit() else text
|
|
||||||
for text in re.split(nsre, s)]
|
|
||||||
|
|
||||||
|
|
||||||
def ordered_active_nics():
|
|
||||||
embedded_nics = []
|
|
||||||
nics = []
|
|
||||||
logger.debug("Finding active nics")
|
|
||||||
for name in glob.iglob(_SYS_CLASS_NET + '/*'):
|
|
||||||
nic = name[(len(_SYS_CLASS_NET) + 1):]
|
|
||||||
if _is_active_nic(nic):
|
|
||||||
if nic.startswith('em') or nic.startswith('eth') or \
|
|
||||||
nic.startswith('eno'):
|
|
||||||
logger.debug("%s is an embedded active nic" % nic)
|
|
||||||
embedded_nics.append(nic)
|
|
||||||
else:
|
|
||||||
logger.debug("%s is an active nic" % nic)
|
|
||||||
nics.append(nic)
|
|
||||||
else:
|
|
||||||
logger.debug("%s is not an active nic" % nic)
|
|
||||||
# NOTE: we could just natural sort all active devices,
|
|
||||||
# but this ensures em, eno, and eth are ordered first
|
|
||||||
# (more backwards compatible)
|
|
||||||
active_nics = (sorted(embedded_nics, key=_natural_sort_key) +
|
|
||||||
sorted(nics, key=_natural_sort_key))
|
|
||||||
logger.debug("Active nics are %s" % active_nics)
|
|
||||||
return active_nics
|
|
||||||
|
|
||||||
|
|
||||||
def diff(filename, data):
|
|
||||||
file_data = get_file_data(filename)
|
|
||||||
logger.debug("Diff file data:\n%s" % file_data)
|
|
||||||
logger.debug("Diff data:\n%s" % data)
|
|
||||||
# convert to string as JSON may have unicode in it
|
|
||||||
return not file_data == data
|
|
@ -1,19 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
# Copyright 2014 Red Hat, Inc.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
|
|
||||||
import pbr.version
|
|
||||||
|
|
||||||
version_info = pbr.version.VersionInfo('os-net-config')
|
|
@ -1,10 +0,0 @@
|
|||||||
pbr>=0.6,!=0.7,<1.0
|
|
||||||
anyjson>=0.3.3
|
|
||||||
Babel>=0.9.6
|
|
||||||
six>=1.6.0
|
|
||||||
eventlet>=0.13.0
|
|
||||||
iso8601>=0.1.9
|
|
||||||
netaddr>=0.7.6
|
|
||||||
oslo.concurrency>=1.4.1 # Apache-2.0
|
|
||||||
oslo.utils>=1.2.0 # Apache-2.0
|
|
||||||
PyYAML>=3.1.0
|
|
54
setup.cfg
54
setup.cfg
@ -1,54 +0,0 @@
|
|||||||
[metadata]
|
|
||||||
name = os-net-config
|
|
||||||
summary = OpenStack network configuration
|
|
||||||
description-file =
|
|
||||||
README.rst
|
|
||||||
author = OpenStack
|
|
||||||
author-email = openstack-dev@lists.openstack.org
|
|
||||||
home-page = http://git.openstack.org/cgit/openstack/os-net-config
|
|
||||||
classifier =
|
|
||||||
Environment :: OpenStack
|
|
||||||
Intended Audience :: Developers
|
|
||||||
Intended Audience :: System Administrators
|
|
||||||
License :: OSI Approved :: Apache Software License
|
|
||||||
Operating System :: OS Independent
|
|
||||||
Programming Language :: Python
|
|
||||||
|
|
||||||
[files]
|
|
||||||
packages =
|
|
||||||
os_net_config
|
|
||||||
|
|
||||||
[global]
|
|
||||||
setup-hooks =
|
|
||||||
pbr.hooks.setup_hook
|
|
||||||
|
|
||||||
[entry_points]
|
|
||||||
console_scripts =
|
|
||||||
os-net-config = os_net_config.cli:main
|
|
||||||
|
|
||||||
[build_sphinx]
|
|
||||||
source-dir = doc/source
|
|
||||||
build-dir = doc/build
|
|
||||||
all_files = 1
|
|
||||||
|
|
||||||
[upload_sphinx]
|
|
||||||
upload-dir = doc/build/html
|
|
||||||
|
|
||||||
[compile_catalog]
|
|
||||||
directory = os_net_config/locale
|
|
||||||
domain = os-net-config
|
|
||||||
|
|
||||||
[update_catalog]
|
|
||||||
domain = os-net-config
|
|
||||||
output_dir = os_net_config/locale
|
|
||||||
input_file = os_net_config/locale/os-net-config.pot
|
|
||||||
|
|
||||||
[extract_messages]
|
|
||||||
keywords = _ gettext ngettext l_ lazy_gettext
|
|
||||||
mapping_file = babel.cfg
|
|
||||||
output_file = os_net_config/locale/os-net-config.pot
|
|
||||||
|
|
||||||
[egg_info]
|
|
||||||
tag_build =
|
|
||||||
tag_date = 0
|
|
||||||
tag_svn_revision = 0
|
|
22
setup.py
22
setup.py
@ -1,22 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# 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
|
|
||||||
|
|
||||||
setuptools.setup(
|
|
||||||
setup_requires=['pbr'],
|
|
||||||
pbr=True)
|
|
@ -1,13 +0,0 @@
|
|||||||
hacking>=0.10.2,<0.11 # Apache-2.0
|
|
||||||
|
|
||||||
coverage>=3.6
|
|
||||||
discover
|
|
||||||
fixtures>=0.3.14
|
|
||||||
python-subunit
|
|
||||||
sphinx>=1.1.2
|
|
||||||
oslosphinx
|
|
||||||
testrepository>=0.0.17
|
|
||||||
testscenarios>=0.4,<0.5
|
|
||||||
testtools>=0.9.32
|
|
||||||
mock>=1.0
|
|
||||||
mox>=0.5.3
|
|
34
tox.ini
34
tox.ini
@ -1,34 +0,0 @@
|
|||||||
[tox]
|
|
||||||
minversion = 1.6
|
|
||||||
envlist = py27,py33,pypy,pep8
|
|
||||||
skipsdist = True
|
|
||||||
|
|
||||||
[testenv]
|
|
||||||
usedevelop = True
|
|
||||||
install_command = pip install -U {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
|
|
||||||
|
|
||||||
[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
|
|
Loading…
Reference in New Issue
Block a user