mirror of
https://github.com/openvswitch/ovs
synced 2025-08-31 22:35:15 +00:00
xenserver: Remove xenserver.
Remove the current xenserver implementation - it is obsolete and since 3.0 we do not support kernel module builds [1]. 1. https://mail.openvswitch.org/pipermail/ovs-dev/2022-July/395789.html [i.maximets] Can be added back if people willing to maintain it will be found. Signed-off-by: Greg Rose <gvrose8192@gmail.com> Signed-off-by: Ilya Maximets <i.maximets@ovn.org>
This commit is contained in:
@@ -21,7 +21,6 @@ DOC_SOURCE = \
|
||||
Documentation/intro/install/rhel.rst \
|
||||
Documentation/intro/install/userspace.rst \
|
||||
Documentation/intro/install/windows.rst \
|
||||
Documentation/intro/install/xenserver.rst \
|
||||
Documentation/tutorials/index.rst \
|
||||
Documentation/tutorials/faucet.rst \
|
||||
Documentation/tutorials/ovs-advanced.rst \
|
||||
|
@@ -44,11 +44,11 @@ Q: What is Open vSwitch?
|
||||
Q: What virtualization platforms can use Open vSwitch?
|
||||
|
||||
A: Open vSwitch can currently run on any Linux-based virtualization
|
||||
platform (kernel 3.10 and newer), including: KVM, VirtualBox, Xen, Xen
|
||||
Cloud Platform, XenServer. As of Linux 3.3 it is part of the mainline
|
||||
kernel. The bulk of the code is written in platform- independent C and is
|
||||
easily ported to other environments. We welcome inquires about integrating
|
||||
Open vSwitch with other virtualization platforms.
|
||||
platform (kernel 3.10 and newer), including: KVM and VirtualBox.
|
||||
As of Linux 3.3 it is part of the mainline kernel. The bulk of the
|
||||
code is written in platform- independent C and is easily ported to
|
||||
other environments. We welcome inquires about integrating Open
|
||||
vSwitch with other virtualization platforms.
|
||||
|
||||
Q: How can I try Open vSwitch?
|
||||
|
||||
@@ -58,8 +58,7 @@ Q: How can I try Open vSwitch?
|
||||
Debian, Ubuntu, Fedora.
|
||||
|
||||
You may also download and run a virtualization platform that already has
|
||||
Open vSwitch integrated. For example, download a recent ISO for XenServer
|
||||
or Xen Cloud Platform. Be aware that the version integrated with a
|
||||
Open vSwitch integrated. Be aware that the version integrated with a
|
||||
particular platform may not be the most recent Open vSwitch release.
|
||||
|
||||
Q: Does Open vSwitch only work on Linux?
|
||||
|
@@ -72,8 +72,7 @@ Both VMs (`vm1` and `vm2`) run on `host1`.
|
||||
Each VM has a single interface that appears as a Linux device (e.g., ``tap0``) on the physical host.
|
||||
|
||||
.. note::
|
||||
For Xen/XenServer, VM interfaces appears as Linux devices with names like
|
||||
``vif1.0``. Other Linux systems may present these interfaces as ``vnet0``,
|
||||
VM interfaces may appear as Linux devices with names like ``vnet0``,
|
||||
``vnet1``, etc.
|
||||
|
||||
Configuration Steps
|
||||
|
@@ -76,8 +76,7 @@ Two Virtual Machines
|
||||
This guide uses two virtual machines - `vm1` and `vm2`- running on `host1`.
|
||||
|
||||
.. note::
|
||||
For Xen/XenServer, VM interfaces appears as Linux devices with names like
|
||||
``vif1.0``. Other Linux systems may present these interfaces as ``vnet0``,
|
||||
VM interfaces may appear as Linux devices with names like ``vnet0``,
|
||||
``vnet1``, etc.
|
||||
|
||||
Configuration Steps
|
||||
|
@@ -83,8 +83,7 @@ Each VM has a single interface that appears as a Linux device (e.g., ``tap0``)
|
||||
on the physical host.
|
||||
|
||||
.. note::
|
||||
For Xen/XenServer, VM interfaces appears as Linux devices with names like
|
||||
``vif1.0``. Other Linux systems may present these interfaces as ``vnet0``,
|
||||
VM interfaces may appear as Linux devices with names like ``vnet0``,
|
||||
``vnet1``, etc.
|
||||
|
||||
Configuration Steps
|
||||
|
@@ -75,8 +75,7 @@ Each VM has a single interface that appears as a Linux device (e.g., ``tap0``)
|
||||
on the physical host.
|
||||
|
||||
.. note::
|
||||
For Xen/XenServer, VM interfaces appears as Linux devices with names like
|
||||
``vif1.0``. Other Linux systems may present these interfaces as ``vnet0``,
|
||||
VM interfaces may appear as Linux devices with names like ``vnet0``,
|
||||
``vnet1``, etc.
|
||||
|
||||
Configuration Steps
|
||||
|
@@ -598,8 +598,8 @@ userspace flows using the ovs-ofctl utility and also uses the
|
||||
``other_config:flow-restore-wait`` column to keep the traffic downtime to the
|
||||
minimum. The ovs-ctl utility's ``force-reload-kmod`` function does all of the
|
||||
above, but also replaces the old kernel module with the new one. Open vSwitch
|
||||
startup scripts for Debian, XenServer and RHEL use ovs-ctl's functions and it
|
||||
is recommended that these functions be used for other software platforms too.
|
||||
startup scripts for Debian and RHEL use ovs-ctl's functions and it is
|
||||
recommended that these functions be used for other software platforms too.
|
||||
|
||||
Reporting Bugs
|
||||
--------------
|
||||
|
@@ -42,7 +42,6 @@ Installation from Source
|
||||
general
|
||||
netbsd
|
||||
windows
|
||||
xenserver
|
||||
userspace
|
||||
dpdk
|
||||
afxdp
|
||||
|
@@ -1,229 +0,0 @@
|
||||
..
|
||||
Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
not use this file except in compliance with the License. You may obtain
|
||||
a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
License for the specific language governing permissions and limitations
|
||||
under the License.
|
||||
|
||||
Convention for heading levels in Open vSwitch documentation:
|
||||
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
|
||||
Avoid deeper levels because they do not render well.
|
||||
|
||||
================================
|
||||
Open vSwitch on Citrix XenServer
|
||||
================================
|
||||
|
||||
This document describes how to build and install Open vSwitch on a Citrix
|
||||
XenServer host. If you want to install Open vSwitch on a generic Linux or BSD
|
||||
host, refer to :doc:`general` instead.
|
||||
|
||||
Open vSwitch should work with XenServer 5.6.100 and later. However, Open
|
||||
vSwitch requires Python 3.4 or later, so using Open vSwitch with XenServer 6.5
|
||||
or earlier requires installing Python 3.x.
|
||||
|
||||
Building
|
||||
--------
|
||||
|
||||
You may build from an Open vSwitch distribution tarball or from an Open vSwitch
|
||||
Git tree. The recommended build environment to build RPMs for Citrix XenServer
|
||||
is the DDK VM available from Citrix.
|
||||
|
||||
1. If you are building from an Open vSwitch Git tree, then you will need to
|
||||
first create a distribution tarball by running::
|
||||
|
||||
$ ./boot.sh
|
||||
$ ./configure
|
||||
$ make dist
|
||||
|
||||
You cannot run this in the DDK VM, because it lacks tools that are necessary
|
||||
to bootstrap the Open vSwitch distribution. Instead, you must run this on a
|
||||
machine that has the tools listed in :ref:`general-install-reqs` as
|
||||
prerequisites for building from a Git tree.
|
||||
|
||||
2. Copy the distribution tarball into ``/usr/src/redhat/SOURCES`` inside
|
||||
the DDK VM.
|
||||
|
||||
3. In the DDK VM, unpack the distribution tarball into a temporary directory
|
||||
and "cd" into the root of the distribution tarball.
|
||||
|
||||
4. To build Open vSwitch userspace, run::
|
||||
|
||||
$ rpmbuild -bb xenserver/openvswitch-xen.spec
|
||||
|
||||
This produces three RPMs in ``/usr/src/redhat/RPMS/i386``:
|
||||
|
||||
- ``openvswitch``
|
||||
- ``openvswitch-modules-xen``
|
||||
- ``openvswitch-debuginfo``
|
||||
|
||||
The above command automatically runs the Open vSwitch unit tests. To
|
||||
disable the unit tests, run::
|
||||
|
||||
$ rpmbuild -bb --without check xenserver/openvswitch-xen.spec
|
||||
|
||||
Build Parameters
|
||||
----------------
|
||||
|
||||
``openvswitch-xen.spec`` needs to know a number of pieces of information about
|
||||
the XenServer kernel. Usually, it can figure these out for itself, but if it
|
||||
does not do it correctly then you can specify them yourself as parameters to
|
||||
the build. Thus, the final ``rpmbuild`` step above can be elaborated as::
|
||||
|
||||
$ VERSION=<Open vSwitch version>
|
||||
$ KERNEL_NAME=<Xen Kernel name>
|
||||
$ KERNEL_VERSION=<Xen Kernel version>
|
||||
$ KERNEL_FLAVOR=<Xen Kernel flavor(suffix)>
|
||||
$ rpmbuild \
|
||||
-D "openvswitch_version $VERSION" \
|
||||
-D "kernel_name $KERNEL_NAME" \
|
||||
-D "kernel_version $KERNEL_VERSION" \
|
||||
-D "kernel_flavor $KERNEL_FLAVOR" \
|
||||
-bb xenserver/openvswitch-xen.spec
|
||||
|
||||
where:
|
||||
|
||||
``<openvswitch version>``
|
||||
is the version number that appears in the name of the Open vSwitch tarball,
|
||||
e.g. 0.90.0.
|
||||
|
||||
``<Xen Kernel name>``
|
||||
is the name of the XenServer kernel package, e.g. ``kernel-xen`` or
|
||||
``kernel-NAME-xen``, without the ``kernel-`` prefix.
|
||||
|
||||
``<Xen Kernel version>``
|
||||
is the output of::
|
||||
|
||||
$ rpm -q --queryformat "%{Version}-%{Release}" <kernel-devel-package>,
|
||||
|
||||
e.g. ``2.6.32.12-0.7.1.xs5.6.100.323.170596``, where
|
||||
``<kernel-devel-package>`` is the name of the ``-devel`` package
|
||||
corresponding to ``<Xen Kernel name>``.
|
||||
|
||||
``<Xen Kernel flavor (suffix)>``
|
||||
is either ``xen`` or ``kdump``, where ``xen`` flavor is the main running
|
||||
kernel flavor and the ``kdump`` flavor is the crashdump kernel flavor.
|
||||
Commonly, one would specify ``xen`` here.
|
||||
|
||||
For XenServer 6.5 or above, the kernel version naming no longer contains
|
||||
KERNEL_FLAVOR. In fact, only providing the ``uname -r`` output is enough. So,
|
||||
the final ``rpmbuild`` step changes to::
|
||||
|
||||
$ KERNEL_UNAME=<`uname -r` output>
|
||||
$ rpmbuild \
|
||||
-D "kenel_uname $KERNEL_UNAME" \
|
||||
-bb xenserver/openvswitch-xen.spec
|
||||
|
||||
Installing Open vSwitch for XenServer
|
||||
-------------------------------------
|
||||
|
||||
To install Open vSwitch on a XenServer host, or to upgrade to a newer version,
|
||||
copy the ``openvswitch`` and ``openvswitch-modules-xen`` RPMs to that host with
|
||||
``scp``, then install them with ``rpm -U``, e.g.::
|
||||
|
||||
$ scp openvswitch-$VERSION-1.i386.rpm \
|
||||
openvswitch-modules-xen-$XEN_KERNEL_VERSION-$VERSION-1.i386.rpm \
|
||||
root@<host>:
|
||||
# Enter <host>'s root password.
|
||||
$ ssh root@<host>
|
||||
# Enter <host>'s root password again.
|
||||
$ rpm -U openvswitch-$VERSION-1.i386.rpm \
|
||||
openvswitch-modules-xen-$XEN_KERNEL_VERSION-$VERSION-1.i386.rpm
|
||||
|
||||
To uninstall Open vSwitch from a XenServer host, remove the packages::
|
||||
|
||||
$ ssh root@<host>
|
||||
# Enter <host>'s root password again.
|
||||
$ rpm -e openvswitch openvswitch-modules-xen-$XEN_KERNEL_VERSION
|
||||
|
||||
After installing or uninstalling Open vSwitch, the XenServer should be rebooted
|
||||
as soon as possible.
|
||||
|
||||
Open vSwitch Boot Sequence on XenServer
|
||||
---------------------------------------
|
||||
|
||||
When Open vSwitch is installed on XenServer, its startup script
|
||||
``/etc/init.d/openvswitch`` runs early in boot. It does roughly the following:
|
||||
|
||||
* Loads the OVS kernel module, openvswitch.
|
||||
|
||||
* Starts ovsdb-server, the OVS configuration database.
|
||||
|
||||
* XenServer expects there to be no bridges configured at startup, but the OVS
|
||||
configuration database likely still has bridges configured from before
|
||||
reboot. To match XenServer expectations, the startup script deletes all
|
||||
configured bridges from the database.
|
||||
|
||||
* Starts ovs-vswitchd, the OVS switching daemon.
|
||||
|
||||
At this point in the boot process, then, there are no Open vSwitch bridges,
|
||||
even though all of the Open vSwitch daemons are running. Later on in boot,
|
||||
``/etc/init.d/management-interface`` (part of XenServer, not Open vSwitch)
|
||||
creates the bridge for the XAPI management interface by invoking
|
||||
``/opt/xensource/libexec/interface-reconfigure``. Normally this program
|
||||
consults XAPI's database to obtain information about how to configure the
|
||||
bridge, but XAPI is not running yet(\*) so it instead consults
|
||||
``/var/xapi/network.dbcache``, which is a cached copy of the most recent
|
||||
network configuration.
|
||||
|
||||
(\*) Even if XAPI were running, if this XenServer node is a pool slave then the
|
||||
query would have to consult the master, which requires network access,
|
||||
which begs the question of how to configure the management interface.
|
||||
|
||||
XAPI starts later on in the boot process. XAPI can then create other bridges
|
||||
on demand using ``/opt/xensource/libexec/interface-reconfigure``. Now that
|
||||
XAPI is running, that program consults XAPI directly instead of reading the
|
||||
cache.
|
||||
|
||||
As part of its own startup, XAPI invokes the Open vSwitch XAPI plugin script
|
||||
``/etc/xapi.d/openvswitch-cfg-update`` passing the ``update`` command. The
|
||||
plugin script does roughly the following:
|
||||
|
||||
* Calls ``/opt/xensource/libexec/interface-reconfigure`` with the ``rewrite``
|
||||
command, to ensure that the network cache is up-to-date.
|
||||
|
||||
* Queries the Open vSwitch manager setting (named ``vswitch_controller``) from
|
||||
the XAPI database for the XenServer pool.
|
||||
|
||||
* If XAPI and OVS are configured for different managers, or if OVS is
|
||||
configured for a manager but XAPI is not, runs ``ovs-vsctl emer-reset`` to
|
||||
bring the Open vSwitch configuration to a known state. One effect of
|
||||
emer-reset is to deconfigure any manager from the OVS database.
|
||||
|
||||
* If XAPI is configured for a manager, configures the OVS manager to match with
|
||||
``ovs-vsctl set-manager``.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
* The Open vSwitch boot sequence only configures an OVS configuration database
|
||||
manager. There is no way to directly configure an OpenFlow controller on
|
||||
XenServer and, as a consequence of the step above that deletes all of the
|
||||
bridges at boot time, controller configuration only persists until XenServer
|
||||
reboot. The configuration database manager can, however, configure
|
||||
controllers for bridges. See the BUGS section of ovs-testcontroller(8) for
|
||||
more information on this topic.
|
||||
|
||||
* The Open vSwitch startup script automatically adds a firewall rule to allow
|
||||
GRE traffic. This rule is needed for the XenServer feature called "Cross-Host
|
||||
Internal Networks" (CHIN) that uses GRE. If a user configures tunnels other
|
||||
than GRE (ex: Geneve, VXLAN, LISP), they will have to either manually add a
|
||||
iptables firewall rule to allow the tunnel traffic or add it through a
|
||||
startup script (Please refer to the "enable-protocol" command in the
|
||||
ovs-ctl(8) manpage).
|
||||
|
||||
Reporting Bugs
|
||||
--------------
|
||||
|
||||
Please report problems to bugs@openvswitch.org.
|
@@ -481,9 +481,8 @@ Files
|
||||
Example
|
||||
=======
|
||||
|
||||
The files ``debian/openvswitch-switch.init`` and
|
||||
``xenserver/etc_init.d_openvswitch`` in the Open vSwitch source
|
||||
distribution are good examples of how to use ``ovs-ctl``.
|
||||
The file ``debian/openvswitch-switch.init`` in the Open vSwitch source
|
||||
distribution is a good example of how to use ``ovs-ctl``.
|
||||
|
||||
See Also
|
||||
========
|
||||
|
@@ -152,11 +152,11 @@ special handling of received packets.
|
||||
|
||||
Several of the physical switches that support LACP block all traffic for ports
|
||||
that are configured to use LACP, until LACP is negotiated with the host. When
|
||||
configuring a LACP bond on a OVS host (eg: XenServer), this means that there
|
||||
will be an interruption of the network connectivity between the time the ports
|
||||
on the physical switch and the bond on the OVS host are configured. The
|
||||
interruption may be relatively long, if different people are responsible for
|
||||
managing the switches and the OVS host.
|
||||
configuring a LACP bond on a OVS host, this means that there will be an
|
||||
interruption of the network connectivity between the time the ports on the
|
||||
physical switch and the bond on the OVS host are configured. The interruption
|
||||
may be relatively long, if different people are responsible for managing the
|
||||
switches and the OVS host.
|
||||
|
||||
Such network connectivity failure can be avoided if LACP can be configured on
|
||||
the OVS host before configuring the physical switch, and having the OVS host
|
||||
|
@@ -29,9 +29,7 @@ This document describes how to integrate Open vSwitch onto a new platform to
|
||||
expose the state of the switch and attached devices for centralized control.
|
||||
(If you are looking to port the switching components of Open vSwitch to a new
|
||||
platform, refer to :doc:`porting`) The focus of this guide is on hypervisors,
|
||||
but many of the interfaces are useful for hardware switches, as well. The
|
||||
XenServer integration is the most mature implementation, so most of the
|
||||
examples are drawn from it.
|
||||
but many of the interfaces are useful for hardware switches, as well.
|
||||
|
||||
The externally visible interface to this integration is platform-agnostic. We
|
||||
encourage anyone who integrates Open vSwitch to use the same interface, because
|
||||
@@ -48,18 +46,15 @@ ensure that these values are correctly populated and maintained.
|
||||
|
||||
An integrator sets the columns in the database by talking to the ovsdb-server
|
||||
daemon. A few of the columns can be set during startup by calling the ovs-ctl
|
||||
tool from inside the startup scripts. The ``xenserver/etc_init.d_openvswitch``
|
||||
tool from inside the startup scripts. The ``rhel/etc_init.d_openvswitch``
|
||||
script provides examples of its use, and the ovs-ctl(8) manpage contains
|
||||
complete documentation. At runtime, ovs-vsctl can be used to set columns in
|
||||
the database. The script ``xenserver/etc_xensource_scripts_vif`` contains
|
||||
examples of its use, and ovs-vsctl(8) manpage contains complete documentation.
|
||||
the database.
|
||||
|
||||
Python and C bindings to the database are provided if deeper integration with a
|
||||
program are needed. The XenServer ovs-xapi-sync daemon
|
||||
(``xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync``) provides an example
|
||||
of using the Python bindings. More information on the python bindings is
|
||||
available at ``python/ovs/db/idl.py``. Information on the C bindings is
|
||||
available at ``lib/ovsdb-idl.h``.
|
||||
program are needed. More information on the python bindings is available at
|
||||
``python/ovs/db/idl.py``. Information on the C bindings is available at
|
||||
``lib/ovsdb-idl.h``.
|
||||
|
||||
The following diagram shows how integration scripts fit into the Open vSwitch
|
||||
architecture:
|
||||
@@ -83,7 +78,6 @@ architecture:
|
||||
| | | |
|
||||
| +---------------------+ | |
|
||||
| | Integration scripts | | |
|
||||
| | (ex: ovs-xapi-sync) | | |
|
||||
| +---------------------+ | |
|
||||
| | Userspace |
|
||||
|----------------------------------------------------------|
|
||||
@@ -105,8 +99,7 @@ individual column, please refer to the ovs-vswitchd.conf.db(5) manpage.
|
||||
The ``Open_vSwitch`` table describes the switch as a whole. The
|
||||
``system_type`` and ``system_version`` columns identify the platform to the
|
||||
controller. The ``external_ids:system-id`` key uniquely identifies the
|
||||
physical host. In XenServer, the system-id will likely be the same as the UUID
|
||||
returned by ``xe host-list``. This key allows controllers to distinguish
|
||||
physical host. This key allows controllers to distinguish
|
||||
between multiple hypervisors.
|
||||
|
||||
Most of this configuration can be done with the ovs-ctl command at startup.
|
||||
@@ -114,7 +107,7 @@ For example:
|
||||
|
||||
::
|
||||
|
||||
$ ovs-ctl --system-type="XenServer" --system-version="6.0.0-50762p" \
|
||||
$ ovs-ctl --system-type="KVM" --system-version="4.18.el8_6" \
|
||||
--system-id="${UUID}" "${other_options}" start
|
||||
|
||||
Alternatively, the ovs-vsctl command may be used to set a particular value at
|
||||
@@ -132,9 +125,7 @@ Bridge table
|
||||
------------
|
||||
|
||||
The Bridge table describes individual bridges within an Open vSwitch instance.
|
||||
The ``external-ids:bridge-id`` key uniquely identifies a particular bridge. In
|
||||
XenServer, this will likely be the same as the UUID returned by ``xe
|
||||
network-list`` for that particular bridge.
|
||||
The ``external-ids:bridge-id`` key uniquely identifies a particular bridge.
|
||||
|
||||
For example, to set the identifier for bridge "br0", the following command can
|
||||
be used:
|
||||
@@ -161,18 +152,14 @@ attached-mac
|
||||
|
||||
This field contains the MAC address of the device attached to the interface.
|
||||
On a hypervisor, this is the MAC address of the interface as seen inside a
|
||||
VM. It does not necessarily correlate to the host-side MAC address. For
|
||||
example, on XenServer, the MAC address on a VIF in the hypervisor is always
|
||||
FE:FF:FF:FF:FF:FF, but inside the VM a normal MAC address is seen.
|
||||
VM. It does not necessarily correlate to the host-side MAC address.
|
||||
|
||||
iface-id
|
||||
|
||||
This field uniquely identifies the interface. In hypervisors, this allows
|
||||
the controller to follow VM network interfaces as VMs migrate. A well-chosen
|
||||
identifier should also allow an administrator or a controller to associate
|
||||
the interface with the corresponding object in the VM management system. For
|
||||
example, the Open vSwitch integration with XenServer by default uses the
|
||||
XenServer assigned UUID for a VIF record as the iface-id.
|
||||
the interface with the corresponding object in the VM management system.
|
||||
|
||||
iface-status
|
||||
|
||||
|
@@ -29,12 +29,6 @@ Tutorials
|
||||
|
||||
Getting started with Open vSwitch (OVS).
|
||||
|
||||
.. TODO(stephenfin): We could really do with a few basic tutorials, along with
|
||||
some more specialized ones (DPDK, XenServer, Windows). The latter could
|
||||
probably be formed from the install guides, but the former will need to be
|
||||
produced from scratch or reproduced from blogs (with permission of the
|
||||
author)
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
|
@@ -482,7 +482,6 @@ include ipsec/automake.mk
|
||||
include vswitchd/automake.mk
|
||||
include ovsdb/automake.mk
|
||||
include rhel/automake.mk
|
||||
include xenserver/automake.mk
|
||||
include python/automake.mk
|
||||
include tutorial/automake.mk
|
||||
include vtep/automake.mk
|
||||
|
2
NEWS
2
NEWS
@@ -86,6 +86,8 @@ v3.0.0 - xx xxx xxxx
|
||||
- Previously deprecated Linux kernel module is now fully removed from
|
||||
the OVS source tree. The version provided with the Linux kernel
|
||||
should be used instead.
|
||||
- XenServer: Support for integration with XenServer has been removed due to
|
||||
lack of maintenance and bitrot.
|
||||
|
||||
|
||||
v2.17.0 - 17 Feb 2022
|
||||
|
26
acinclude.m4
26
acinclude.m4
@@ -588,32 +588,6 @@ AC_DEFUN([OVS_CONDITIONAL_CC_OPTION_DEFINE],
|
||||
[Define to 1 if compiler supports the '$1' option.])
|
||||
fi])
|
||||
|
||||
dnl Check for too-old XenServer.
|
||||
AC_DEFUN([OVS_CHECK_XENSERVER_VERSION],
|
||||
[AC_CACHE_CHECK([XenServer release], [ovs_cv_xsversion],
|
||||
[if test -e /etc/redhat-release; then
|
||||
ovs_cv_xsversion=`sed -n 's/^XenServer DDK release \([[^-]]*\)-.*/\1/p' /etc/redhat-release`
|
||||
fi
|
||||
if test -z "$ovs_cv_xsversion"; then
|
||||
ovs_cv_xsversion=none
|
||||
fi])
|
||||
case $ovs_cv_xsversion in
|
||||
none)
|
||||
;;
|
||||
|
||||
[[1-9]][[0-9]]* | dnl XenServer 10 or later
|
||||
[[6-9]]* | dnl XenServer 6 or later
|
||||
5.[[7-9]]* | dnl XenServer 5.7 or later
|
||||
5.6.[[1-9]][[0-9]][[0-9]][[0-9]]* | dnl XenServer 5.6.1000 or later
|
||||
5.6.[[2-9]][[0-9]][[0-9]]* | dnl XenServer 5.6.200 or later
|
||||
5.6.1[[0-9]][[0-9]]) dnl Xenserver 5.6.100 or later
|
||||
;;
|
||||
|
||||
*)
|
||||
AC_MSG_ERROR([This appears to be XenServer $ovs_cv_xsversion, but only XenServer 5.6.100 or later is supported. (If you are really using a supported version of XenServer, you may override this error message by specifying 'ovs_cv_xsversion=5.6.100' on the "configure" command line.)])
|
||||
;;
|
||||
esac])
|
||||
|
||||
dnl OVS_CHECK_SPARSE_TARGET
|
||||
dnl
|
||||
dnl The "cgcc" script from "sparse" isn't very good at detecting the
|
||||
|
@@ -11,7 +11,6 @@
|
||||
^lib/strsep\.c$
|
||||
^third-party/
|
||||
^windows/
|
||||
^xenserver/
|
||||
^debian/README.Debian$
|
||||
^debian/copyright.in$
|
||||
^debian/openvswitch-switch.init$
|
||||
|
@@ -124,7 +124,6 @@ OVS_CHECK_BACKTRACE
|
||||
OVS_CHECK_PERF_EVENT
|
||||
OVS_CHECK_VALGRIND
|
||||
OVS_CHECK_SOCKET_LIBS
|
||||
OVS_CHECK_XENSERVER_VERSION
|
||||
OVS_CHECK_GROFF
|
||||
OVS_CHECK_TLS
|
||||
OVS_CHECK_ATOMIC_LIBS
|
||||
|
@@ -1,117 +0,0 @@
|
||||
# Copyright (c) 2011, 2012 Nicira, 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 re
|
||||
|
||||
|
||||
def xapi_local():
|
||||
return Session()
|
||||
|
||||
|
||||
class Session(object):
|
||||
def __init__(self):
|
||||
self.xenapi = XenAPI()
|
||||
|
||||
|
||||
class Failure(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class XenAPI(object):
|
||||
def __init__(self):
|
||||
self.network = Network()
|
||||
self.pool = Pool()
|
||||
self.VIF = VIF()
|
||||
self.VM = VM()
|
||||
|
||||
def login_with_password(self, unused_username, unused_password):
|
||||
pass
|
||||
|
||||
|
||||
class RecordRef(object):
|
||||
def __init__(self, attrs):
|
||||
self.attrs = attrs
|
||||
|
||||
|
||||
class Table(object):
|
||||
def __init__(self, records):
|
||||
self.records = records
|
||||
|
||||
def get_all(self):
|
||||
return [RecordRef(rec) for rec in self.records]
|
||||
|
||||
def get_all_records_where(self, condition):
|
||||
k, v = re.match(r'field "([^"]*)"="([^"]*)"$', condition).groups()
|
||||
d = {}
|
||||
|
||||
# I'm sure that the keys used in the dictionary below are wrong
|
||||
# but I can't find any documentation on get_all_records_where
|
||||
# and this satisfies the current test case.
|
||||
i = 0
|
||||
for rec in self.records:
|
||||
if rec[k] == v:
|
||||
d[i] = rec
|
||||
i += 1
|
||||
return d
|
||||
|
||||
def get_by_uuid(self, uuid):
|
||||
recs = [rec for rec in self.records if rec["uuid"] == uuid]
|
||||
if len(recs) != 1:
|
||||
raise Failure("No record with UUID %s" % uuid)
|
||||
return RecordRef(recs[0])
|
||||
|
||||
def get_record(self, record_ref):
|
||||
return record_ref.attrs
|
||||
|
||||
|
||||
class Network(Table):
|
||||
__records = ({"uuid": "9b66c68b-a74e-4d34-89a5-20a8ab352d1e",
|
||||
"bridge": "xenbr0",
|
||||
"other_config":
|
||||
{"vswitch-controller-fail-mode": "secure",
|
||||
"nicira-bridge-id": "custom bridge ID"}},
|
||||
{"uuid": "e1c9019d-375b-45ac-a441-0255dd2247de",
|
||||
"bridge": "xenbr1",
|
||||
"other_config":
|
||||
{"vswitch-disable-in-band": "true"}})
|
||||
|
||||
def __init__(self):
|
||||
Table.__init__(self, Network.__records)
|
||||
|
||||
|
||||
class Pool(Table):
|
||||
__records = ({"uuid": "7a793edf-e5f4-4994-a0f9-cee784c0cda3",
|
||||
"other_config":
|
||||
{"vswitch-controller-fail-mode": "secure"}},)
|
||||
|
||||
def __init__(self):
|
||||
Table.__init__(self, Pool.__records)
|
||||
|
||||
|
||||
class VIF(Table):
|
||||
__records = ({"uuid": "6ab1b260-398e-49ba-827b-c7696108964c",
|
||||
"other_config":
|
||||
{"nicira-iface-id": "custom iface ID"}},)
|
||||
|
||||
def __init__(self):
|
||||
Table.__init__(self, VIF.__records)
|
||||
|
||||
|
||||
class VM(Table):
|
||||
__records = ({"uuid": "fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8",
|
||||
"other_config":
|
||||
{"nicira-vm-id": "custom vm ID"}},)
|
||||
|
||||
def __init__(self):
|
||||
Table.__init__(self, VM.__records)
|
@@ -99,11 +99,9 @@ TESTSUITE_AT = \
|
||||
tests/ovsdb-lock.at \
|
||||
tests/ovsdb-rbac.at \
|
||||
tests/ovs-vsctl.at \
|
||||
tests/ovs-xapi-sync.at \
|
||||
tests/pytest.at \
|
||||
tests/stp.at \
|
||||
tests/rstp.at \
|
||||
tests/interface-reconfigure.at \
|
||||
tests/vlog.at \
|
||||
tests/vtep-ctl.at \
|
||||
tests/auto-attach.at \
|
||||
@@ -526,7 +524,6 @@ CHECK_PYFILES = \
|
||||
tests/test-ovsdb.py \
|
||||
tests/test-reconnect.py \
|
||||
tests/test-stream.py \
|
||||
tests/MockXenAPI.py \
|
||||
tests/test-unix-socket.py \
|
||||
tests/test-unixctl.py \
|
||||
tests/test-vlog.py \
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,74 +0,0 @@
|
||||
AT_BANNER([ovs-xapi-sync])
|
||||
|
||||
AT_SETUP([ovs-xapi-sync])
|
||||
|
||||
# Mock up the XenAPI.
|
||||
cp "$top_srcdir/tests/MockXenAPI.py" XenAPI.py
|
||||
PYTHONPATH=`pwd`:$PYTHONPATH
|
||||
export PYTHONPATH
|
||||
|
||||
cp "$top_srcdir/vswitchd/vswitch.ovsschema" .
|
||||
|
||||
cp "$top_srcdir/xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync" \
|
||||
ovs-xapi-sync
|
||||
|
||||
on_exit 'kill `cat pid ovs-xapi-sync.pid`'
|
||||
|
||||
mkdir var var/run
|
||||
touch var/run/xapi_init_complete.cookie
|
||||
|
||||
ovs_vsctl () {
|
||||
ovs-vsctl --no-wait -vreconnect:emer "$@"
|
||||
}
|
||||
|
||||
# Start ovsdb-server.
|
||||
OVS_VSCTL_SETUP
|
||||
|
||||
# Start ovs-xapi-sync.
|
||||
AT_CHECK([$PYTHON3 ./ovs-xapi-sync "--pidfile=ovs-xapi-sync.pid" \
|
||||
"--root-prefix=`pwd`" unix:db.sock >log 2>&1 &])
|
||||
AT_CAPTURE_FILE([log])
|
||||
|
||||
# Add bridges and check ovs-xapi-sync's work.
|
||||
AT_CHECK([ovs_vsctl -- add-br xenbr0 -- add-br xenbr1])
|
||||
OVS_WAIT_UNTIL([test "X`ovs_vsctl get bridge xenbr0 fail-mode`" != "X[[]]"])
|
||||
AT_CHECK([ovs_vsctl \
|
||||
-- get bridge xenbr0 fail-mode other-config external-ids \
|
||||
-- get bridge xenbr1 fail-mode other-config external-ids], [0],
|
||||
[[secure
|
||||
{}
|
||||
{bridge-id="custom bridge ID"}
|
||||
secure
|
||||
{disable-in-band="true"}
|
||||
{}
|
||||
]])
|
||||
|
||||
# Add vif and check daemon's work.
|
||||
AT_CHECK([ovs_vsctl \
|
||||
-- add-port xenbr0 vif1.0 \
|
||||
-- set Interface vif1.0 'external-ids={attached-mac="00:11:22:33:44:55", xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"'}])
|
||||
OVS_WAIT_UNTIL([ovs_vsctl get interface vif1.0 external-ids:iface-id >/dev/null 2>&1])
|
||||
AT_CHECK([ovs_vsctl get interface vif1.0 external-ids], [0],
|
||||
[{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=active, vm-id="custom vm ID", xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"}
|
||||
])
|
||||
|
||||
# Add corresponding tap and check daemon's work.
|
||||
AT_CHECK([ovs_vsctl add-port xenbr0 tap1.0])
|
||||
OVS_WAIT_UNTIL([ovs_vsctl get interface tap1.0 external-ids:iface-id >/dev/null 2>&1])
|
||||
AT_CHECK([ovs_vsctl \
|
||||
-- get interface vif1.0 external-ids \
|
||||
-- get interface tap1.0 external-ids], [0],
|
||||
[{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=inactive, vm-id="custom vm ID", xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"}
|
||||
{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=active, vm-id="custom vm ID", xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"}
|
||||
])
|
||||
|
||||
# Remove corresponding tap and check daemon's work.
|
||||
AT_CHECK([ovs_vsctl del-port tap1.0])
|
||||
OVS_WAIT_UNTIL([test `ovs_vsctl get interface vif1.0 external-ids:iface-status` = active])
|
||||
AT_CHECK([ovs_vsctl get interface vif1.0 external-ids], [0],
|
||||
[{attached-mac="00:11:22:33:44:55", iface-id="custom iface ID", iface-status=active, vm-id="custom vm ID", xs-network-uuid="9b66c68b-a74e-4d34-89a5-20a8ab352d1e", xs-vif-uuid="6ab1b260-398e-49ba-827b-c7696108964c", xs-vm-uuid="fcb8a3f6-dc04-41d2-8b8a-55afd2b755b8"}
|
||||
])
|
||||
|
||||
OVSDB_SERVER_SHUTDOWN
|
||||
|
||||
AT_CLEANUP
|
@@ -67,8 +67,6 @@ m4_include([tests/bridge.at])
|
||||
m4_include([tests/netdev-type.at])
|
||||
m4_include([tests/ovsdb.at])
|
||||
m4_include([tests/ovs-vsctl.at])
|
||||
m4_include([tests/ovs-xapi-sync.at])
|
||||
m4_include([tests/interface-reconfigure.at])
|
||||
m4_include([tests/stp.at])
|
||||
m4_include([tests/rstp.at])
|
||||
m4_include([tests/vlog.at])
|
||||
|
@@ -1205,3 +1205,21 @@ Datapath actions: push_eth(src=00:00:00:00:00:00,dst=00:00:00:00:00:00),1
|
||||
|
||||
OVS_VSWITCHD_STOP
|
||||
AT_CLEANUP
|
||||
|
||||
dnl This test configures two tunnels, then deletes the second and re-uses its
|
||||
dnl name for different types of ports. This was introduced to detect errors
|
||||
dnl where port configuration persists even when the port is deleted and
|
||||
dnl readded.
|
||||
AT_SETUP([tunnel - re-create port with different types])
|
||||
OVS_VSWITCHD_START(
|
||||
[add-port br0 p0 -- set int p0 type=gre options:remote_ip=127.0.0.1 -- \
|
||||
add-port br0 p1 -- set int p1 type=dummy -- \
|
||||
add-port br0 p2 -- set int p2 type=dummy])
|
||||
|
||||
AT_CHECK([ovs-vsctl set int p1 type=gre options:remote_ip=127.0.0.1])
|
||||
AT_CHECK([ovs-vsctl del-port p1])
|
||||
AT_CHECK([ovs-vsctl add-port br0 p1 -- set int p1 type=dummy])
|
||||
|
||||
OVS_APP_EXIT_AND_WAIT([ovs-vswitchd])
|
||||
OVS_APP_EXIT_AND_WAIT([ovsdb-server])]
|
||||
AT_CLEANUP
|
||||
|
@@ -8,9 +8,7 @@ vswitchd_ovs_vswitchd_SOURCES = \
|
||||
vswitchd/bridge.h \
|
||||
vswitchd/ovs-vswitchd.c \
|
||||
vswitchd/system-stats.c \
|
||||
vswitchd/system-stats.h \
|
||||
vswitchd/xenserver.c \
|
||||
vswitchd/xenserver.h
|
||||
vswitchd/system-stats.h
|
||||
vswitchd_ovs_vswitchd_LDADD = \
|
||||
ofproto/libofproto.la \
|
||||
lib/libsflow.la \
|
||||
|
@@ -70,7 +70,6 @@
|
||||
#include "util.h"
|
||||
#include "unixctl.h"
|
||||
#include "lib/vswitch-idl.h"
|
||||
#include "xenserver.h"
|
||||
#include "vlan-bitmap.h"
|
||||
|
||||
VLOG_DEFINE_THIS_MODULE(bridge);
|
||||
@@ -299,9 +298,7 @@ static void bridge_configure_remotes(struct bridge *,
|
||||
static void bridge_pick_local_hw_addr(struct bridge *, struct eth_addr *ea,
|
||||
struct iface **hw_addr_iface);
|
||||
static uint64_t bridge_pick_datapath_id(struct bridge *,
|
||||
const struct eth_addr bridge_ea,
|
||||
struct iface *hw_addr_iface);
|
||||
static uint64_t dpid_from_hash(const void *, size_t nbytes);
|
||||
const struct eth_addr bridge_ea);
|
||||
static bool bridge_has_bond_fake_iface(const struct bridge *,
|
||||
const char *name);
|
||||
static bool port_is_bond_fake_iface(const struct port *);
|
||||
@@ -1329,7 +1326,7 @@ bridge_configure_datapath_id(struct bridge *br)
|
||||
}
|
||||
br->ea = ea;
|
||||
|
||||
dpid = bridge_pick_datapath_id(br, ea, hw_addr_iface);
|
||||
dpid = bridge_pick_datapath_id(br, ea);
|
||||
if (dpid != ofproto_get_datapath_id(br->ofproto)) {
|
||||
VLOG_INFO("bridge %s: using datapath ID %016"PRIx64, br->name, dpid);
|
||||
ofproto_set_datapath_id(br->ofproto, dpid);
|
||||
@@ -2362,14 +2359,10 @@ bridge_pick_local_hw_addr(struct bridge *br, struct eth_addr *ea,
|
||||
}
|
||||
|
||||
/* Choose and returns the datapath ID for bridge 'br' given that the bridge
|
||||
* Ethernet address is 'bridge_ea'. If 'bridge_ea' is the Ethernet address of
|
||||
* an interface on 'br', then that interface must be passed in as
|
||||
* 'hw_addr_iface'; if 'bridge_ea' was derived some other way, then
|
||||
* 'hw_addr_iface' must be passed in as a null pointer. */
|
||||
* Ethernet address is 'bridge_ea'. */
|
||||
static uint64_t
|
||||
bridge_pick_datapath_id(struct bridge *br,
|
||||
const struct eth_addr bridge_ea,
|
||||
struct iface *hw_addr_iface)
|
||||
const struct eth_addr bridge_ea)
|
||||
{
|
||||
/*
|
||||
* The procedure for choosing a bridge MAC address will, in the most
|
||||
@@ -2391,46 +2384,9 @@ bridge_pick_datapath_id(struct bridge *br,
|
||||
return dpid;
|
||||
}
|
||||
|
||||
if (!hw_addr_iface) {
|
||||
/*
|
||||
* A purely internal bridge, that is, one that has no non-virtual
|
||||
* network devices on it at all, is difficult because it has no
|
||||
* natural unique identifier at all.
|
||||
*
|
||||
* When the host is a XenServer, we handle this case by hashing the
|
||||
* host's UUID with the name of the bridge. Names of bridges are
|
||||
* persistent across XenServer reboots, although they can be reused if
|
||||
* an internal network is destroyed and then a new one is later
|
||||
* created, so this is fairly effective.
|
||||
*
|
||||
* When the host is not a XenServer, we punt by using a random MAC
|
||||
* address on each run.
|
||||
*/
|
||||
const char *host_uuid = xenserver_get_host_uuid();
|
||||
if (host_uuid) {
|
||||
char *combined = xasprintf("%s,%s", host_uuid, br->name);
|
||||
dpid = dpid_from_hash(combined, strlen(combined));
|
||||
free(combined);
|
||||
return dpid;
|
||||
}
|
||||
}
|
||||
|
||||
return eth_addr_to_uint64(bridge_ea);
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
dpid_from_hash(const void *data, size_t n)
|
||||
{
|
||||
union {
|
||||
uint8_t bytes[SHA1_DIGEST_SIZE];
|
||||
struct eth_addr ea;
|
||||
} hash;
|
||||
|
||||
sha1_bytes(data, n, hash.bytes);
|
||||
eth_addr_mark_random(&hash.ea);
|
||||
return eth_addr_to_uint64(hash.ea);
|
||||
}
|
||||
|
||||
static void
|
||||
iface_refresh_netdev_status(struct iface *iface)
|
||||
{
|
||||
|
@@ -1,79 +0,0 @@
|
||||
/* Copyright (c) 2009, 2010, 2013 Nicira, 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.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include "xenserver.h"
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "openvswitch/dynamic-string.h"
|
||||
#include "process.h"
|
||||
#include "util.h"
|
||||
#include "openvswitch/vlog.h"
|
||||
|
||||
VLOG_DEFINE_THIS_MODULE(xenserver);
|
||||
|
||||
/* If running on a XenServer, the XenServer host UUID as a 36-character string,
|
||||
* otherwise null. */
|
||||
static char *host_uuid;
|
||||
|
||||
static void
|
||||
read_host_uuid(void)
|
||||
{
|
||||
static const char filename[] = "/etc/xensource-inventory";
|
||||
char line[128];
|
||||
FILE *file;
|
||||
|
||||
file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
if (errno == ENOENT) {
|
||||
VLOG_DBG("not running on a XenServer");
|
||||
} else {
|
||||
VLOG_INFO("%s: open: %s", filename, ovs_strerror(errno));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
while (fgets(line, sizeof line, file)) {
|
||||
static const char leader[] = "INSTALLATION_UUID='";
|
||||
const int leader_len = strlen(leader);
|
||||
const int uuid_len = 36;
|
||||
static const char trailer[] = "'\n";
|
||||
const int trailer_len = strlen(trailer);
|
||||
|
||||
if (strlen(line) == leader_len + uuid_len + trailer_len
|
||||
&& !memcmp(line, leader, leader_len)
|
||||
&& !memcmp(line + leader_len + uuid_len, trailer, trailer_len)) {
|
||||
host_uuid = xmemdup0(line + leader_len, uuid_len);
|
||||
VLOG_INFO("running on XenServer, host-uuid %s", host_uuid);
|
||||
fclose(file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
VLOG_ERR("%s: INSTALLATION_UUID not found", filename);
|
||||
}
|
||||
|
||||
const char *
|
||||
xenserver_get_host_uuid(void)
|
||||
{
|
||||
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
||||
pthread_once(&once, read_host_uuid);
|
||||
return host_uuid;
|
||||
}
|
||||
|
@@ -1,21 +0,0 @@
|
||||
/* Copyright (c) 2009 Nicira, 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.
|
||||
*/
|
||||
|
||||
#ifndef VSWITCHD_XENSERVER_H
|
||||
#define VSWITCHD_XENSERVER_H 1
|
||||
|
||||
const char *xenserver_get_host_uuid(void);
|
||||
|
||||
#endif /* xenserver.h */
|
1
xenserver/.gitignore
vendored
1
xenserver/.gitignore
vendored
@@ -1 +0,0 @@
|
||||
/openvswitch-xen.spec
|
339
xenserver/GPLv2
339
xenserver/GPLv2
@@ -1,339 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
@@ -1,518 +0,0 @@
|
||||
As a special exception to the GNU Lesser General Public License, you
|
||||
may link, statically or dynamically, a "work that uses the Library"
|
||||
with a publicly distributed version of the Library to produce an
|
||||
executable file containing portions of the Library, and distribute
|
||||
that executable file under terms of your choice, without any of the
|
||||
additional requirements listed in clause 6 of the GNU Lesser General
|
||||
Public License. By "a publicly distributed version of the Library",
|
||||
we mean either the unmodified Library as distributed, or a
|
||||
modified version of the Library that is distributed under the
|
||||
conditions defined in clause 3 of the GNU Library General Public
|
||||
License. This exception does not however invalidate any other reasons
|
||||
why the executable file might be covered by the GNU Lesser General
|
||||
Public License.
|
||||
|
||||
------------
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
[This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.]
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
not price. Our General Public Licenses are designed to make sure that
|
||||
you have the freedom to distribute copies of free software (and charge
|
||||
for this service if you wish); that you receive source code or can get
|
||||
it if you want it; that you can change the software and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, complete source code means
|
||||
all the source code for all modules it contains, plus any associated
|
||||
interface definition files, plus the scripts used to control compilation
|
||||
and installation of the library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete source code as you receive it, in any medium, provided that
|
||||
you conspicuously and appropriately publish on each copy an
|
||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
||||
all the notices that refer to this License and to the absence of any
|
||||
warranty; and distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy,
|
||||
and you may at your option offer warranty protection in exchange for a
|
||||
fee.
|
||||
|
||||
2. You may modify your copy or copies of the Library or any portion
|
||||
of it, thus forming a work based on the Library, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Library, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote
|
||||
it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you accompany
|
||||
it with the complete corresponding machine-readable source code, which
|
||||
must be distributed under the terms of Sections 1 and 2 above on a
|
||||
medium customarily used for software interchange.
|
||||
|
||||
If distribution of object code is made by offering access to copy
|
||||
from a designated place, then offering equivalent access to copy the
|
||||
source code from the same place satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be distributed need not include anything that is
|
||||
normally distributed (in either source or binary form) with the major
|
||||
components (compiler, kernel, and so on) of the operating system on
|
||||
which the executable runs, unless that component itself accompanies
|
||||
the executable.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library is void, and will automatically terminate your
|
||||
rights under this License. However, parties who have received copies,
|
||||
or rights, from you under this License will not have their licenses
|
||||
terminated so long as such parties remain in full compliance.
|
||||
|
||||
9. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
subject to these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties with
|
||||
this License.
|
||||
|
||||
11. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Library.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under any
|
||||
particular circumstance, the balance of the section is intended to apply,
|
||||
and the section as a whole is intended to apply in other circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library under this License may add
|
||||
an explicit geographical distribution limitation excluding those countries,
|
||||
so that distribution is permitted only in or among countries not thus
|
||||
excluded. In such case, this License incorporates the limitation as if
|
||||
written in the body of this License.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser General Public License from time to time.
|
||||
Such new versions will be similar in spirit to the present version,
|
||||
but may differ in detail to address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Library
|
||||
specifies a version number of this License which applies to it and
|
||||
"any later version", you have the option of following the terms and
|
||||
conditions either of that version or of any later version published by
|
||||
the Free Software Foundation. If the Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
write to the author to ask for permission. For software which is
|
||||
copyrighted by the Free Software Foundation, write to the Free
|
||||
Software Foundation; we sometimes make exceptions for this. Our
|
||||
decision will be guided by the two goals of preserving the free status
|
||||
of all derivatives of our free software and of promoting the sharing
|
||||
and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. It is
|
||||
safest to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least the
|
||||
"copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the library's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
@@ -1,175 +0,0 @@
|
||||
..
|
||||
Copyright (C) 2009, 2010, 2011 Nicira, 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.
|
||||
|
||||
Convention for heading levels in Open vSwitch documentation:
|
||||
|
||||
======= Heading 0 (reserved for the title in a document)
|
||||
------- Heading 1
|
||||
~~~~~~~ Heading 2
|
||||
+++++++ Heading 3
|
||||
''''''' Heading 4
|
||||
|
||||
Avoid deeper levels because they do not render well.
|
||||
|
||||
================
|
||||
XenServer README
|
||||
================
|
||||
|
||||
This directory contains files for seamless integration of Open vSwitch on
|
||||
Citrix XenServer hosts managed by the Citrix management tools.
|
||||
|
||||
Files in this directory are licensed on a file-by-file basis. Refer to each
|
||||
file for details.
|
||||
|
||||
Most of the files in this directory are installed on a XenServer system under
|
||||
the same name; underscores are replaced by slashes. The files are:
|
||||
|
||||
etc_init.d_openvswitch
|
||||
Initializes Open vSwitch at boot and shuts it down at shutdown.
|
||||
|
||||
etc_init.d_openvswitch-xapi-update
|
||||
Init script to ensure openvswitch-cfg-update is called for the current host
|
||||
at boot.
|
||||
|
||||
etc_logrotate.d_openvswitch
|
||||
Ensures that logs in /var/log/openvswitch are rotated periodically and that
|
||||
appropriate daemons reopen their log files at that point.
|
||||
|
||||
etc_profile.d_openvswitch.sh
|
||||
Open vSwitch-related shell functions for the administrator's convenience.
|
||||
|
||||
etc_xapi.d_plugins_openvswitch-cfg-update
|
||||
xapi plugin script to update the cache of configuration items in the
|
||||
ovs-vswitchd configuration that are managed in the xapi database when
|
||||
integrated with Citrix management tools.
|
||||
|
||||
etc_xensource_scripts_vif
|
||||
Open vSwitch-aware replacement for Citrix script of the same name.
|
||||
|
||||
openvswitch-xen.spec
|
||||
spec file for building RPMs to install on a XenServer host.
|
||||
|
||||
opt_xensource_libexec_interface-reconfigure
|
||||
Open vSwitch-aware replacements for Citrix script of the same names.
|
||||
|
||||
opt_xensource_libexec_InterfaceReconfigureBridge.py
|
||||
See above.
|
||||
|
||||
opt_xensource_libexec_InterfaceReconfigure.py
|
||||
See above.
|
||||
|
||||
opt_xensource_libexec_InterfaceReconfigureVswitch.py
|
||||
See above.
|
||||
|
||||
usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py
|
||||
xsconsole plugin to configure the pool-wide configuration keys used to
|
||||
control Open vSwitch when integrated with Citrix management tools.
|
||||
|
||||
usr_share_openvswitch_scripts_ovs-xapi-sync
|
||||
Daemon to monitor the external_ids columns of the Bridge and Interface OVSDB
|
||||
tables for changes that require interrogating XAPI.
|
||||
|
||||
usr_share_openvswitch_scripts_sysconfig.template
|
||||
Template for Open vSwitch's /etc/sysconfig/openvswitch configuration file.
|
||||
|
||||
Open vSwitch installs a number of xen-bugtool extensions in
|
||||
``/etc/xensource/bugtool`` to gather additional information useful for
|
||||
debugging. The sources for the extensions are in
|
||||
``../utilities/bugtool/plugins``:
|
||||
|
||||
kernel-info/openvswitch.xml
|
||||
Collect kernel information relevant to Open vSwitch, such as slabinfo.
|
||||
|
||||
network-status/openvswitch.xml
|
||||
Collect networking information relevant to Open vSwitch. Runs the following
|
||||
scripts, which are described below:
|
||||
|
||||
* ovs-bugtool-bfd-show
|
||||
* ovs-bugtool-cfm-show
|
||||
* ovs-bugtool-fdb-show
|
||||
* ovs-bugtool-lacp-show
|
||||
* ovs-bugtool-list-dbs
|
||||
* ovs-bugtool-ovsdb-dump
|
||||
* ovs-bugtool-tc-class-show
|
||||
* ovs-bugtool-bond-show
|
||||
* ovs-bugtool-ovs-ofctl-show
|
||||
* ovs-bugtool-ovs-ofctl-dump-flows
|
||||
* ovs-bugtool-ovs-appctl-dpif
|
||||
* ovs-bugtool-coverage-show
|
||||
* ovs-bugtool-memory-show
|
||||
* ovs-bugtool-vsctl-show
|
||||
* ovs-bugtool-conntrack-dump
|
||||
|
||||
system-configuration/openvswitch.xml
|
||||
Collect system configuration information relevant to Open vSwitch, including
|
||||
timezone. Runs the following script which is described below:
|
||||
|
||||
* ovs-bugtool-daemons-ver
|
||||
|
||||
system-configuration.xml
|
||||
Collect system configuration data. This category is configured to collect up
|
||||
to 1Mb of data, take up to 60 seconds to collect data, run every time and is
|
||||
hidden from display in XenCenter.
|
||||
|
||||
A number of scripts are installed in ``/usr/share/openvswitch/scripts`` to
|
||||
assist Open vSwitch's xen-bugtool extensions. The sources for the scripts are
|
||||
located in ``../utilities/bugtool``:
|
||||
|
||||
ovs-bugtool-bfd-show
|
||||
Script to dump detailed BFD information for all enabled interfaces.
|
||||
|
||||
ovs-bugtool-cfm-show
|
||||
Script to dump detailed CFM information for all enabled interfaces.
|
||||
|
||||
ovs-bugtool-fdb-show
|
||||
Script to collect a summary of learned MACs for each bridge.
|
||||
|
||||
ovs-bugtool-lacp-show
|
||||
Script to dump detailed LACP information for all enabled ports.
|
||||
|
||||
ovs-bugtool-list-dbs
|
||||
Script to list the databases controlled by ovsdb-server.
|
||||
|
||||
ovs-bugtool-ovsdb-dump
|
||||
Script to dump contents of Open vSwitch configuration database in
|
||||
comma-separated value format.
|
||||
|
||||
ovs-bugtool-tc-class-show
|
||||
Script to dump tc class configuration for all network interfaces.
|
||||
|
||||
ovs-bugtool-ovs-ofctl-show
|
||||
Script to dump information about flow tables and ports of each bridge.
|
||||
|
||||
ovs-bugtool-ovs-ofctl-dump-flows
|
||||
Script to dump openflow flows of each bridge.
|
||||
|
||||
ovs-bugtool-ovs-appctl-dpif
|
||||
Script to collect a summary of configured datapaths and datapath flows.
|
||||
|
||||
ovs-bugtool-coverage-show
|
||||
Script to count the number of times particular events occur during
|
||||
ovs-vswitchd's runtime.
|
||||
|
||||
ovs-bugtool-memory-show
|
||||
Script to show some basic statistics about ovs-vswitchd's memory usage.
|
||||
|
||||
ovs-bugtool-vsctl-show
|
||||
Script to show a brief overview of the database contents.
|
||||
|
||||
ovs-bugtool-conntrack-dump
|
||||
Script to show all the connection entries in the tracker.
|
||||
|
||||
ovs-bugtool-daemons-ver
|
||||
Script to dump version information for all Open vSwitch daemons.
|
@@ -1,35 +0,0 @@
|
||||
# Copyright (C) 2009, 2010, 2011, 2012, 2014 Nicira, Inc.
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without warranty of any kind.
|
||||
|
||||
EXTRA_DIST += \
|
||||
xenserver/GPLv2 \
|
||||
xenserver/LICENSE \
|
||||
xenserver/README.rst \
|
||||
xenserver/automake.mk \
|
||||
xenserver/etc_init.d_openvswitch \
|
||||
xenserver/etc_init.d_openvswitch-xapi-update \
|
||||
xenserver/etc_logrotate.d_openvswitch \
|
||||
xenserver/etc_profile.d_openvswitch.sh \
|
||||
xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \
|
||||
xenserver/etc_xensource_scripts_vif \
|
||||
xenserver/openvswitch-xen.spec \
|
||||
xenserver/openvswitch-xen.spec.in \
|
||||
xenserver/opt_xensource_libexec_InterfaceReconfigure.py \
|
||||
xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \
|
||||
xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \
|
||||
xenserver/opt_xensource_libexec_interface-reconfigure \
|
||||
xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
|
||||
xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \
|
||||
xenserver/usr_share_openvswitch_scripts_sysconfig.template
|
||||
|
||||
FLAKE8_PYFILES += \
|
||||
xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync
|
||||
|
||||
$(srcdir)/xenserver/openvswitch-xen.spec: xenserver/openvswitch-xen.spec.in $(top_builddir)/config.status
|
||||
$(AM_V_GEN)($(ro_shell) && sed -e 's,[@]VERSION[@],$(VERSION),g') \
|
||||
< $(srcdir)/xenserver/$(@F).in > $(@F).tmp || exit 1; \
|
||||
if cmp -s $(@F).tmp $@; then touch $@; rm $(@F).tmp; else mv $(@F).tmp $@; fi
|
@@ -1,154 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# openvswitch
|
||||
#
|
||||
# chkconfig: 2345 09 91
|
||||
# description: Manage Open vSwitch kernel modules and user-space daemons
|
||||
|
||||
# Copyright (C) 2009, 2010, 2011 Nicira, 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.
|
||||
### BEGIN INIT INFO
|
||||
# Provides: openvswitch-switch
|
||||
# Required-Start:
|
||||
# Required-Stop:
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Open vSwitch switch
|
||||
### END INIT INFO
|
||||
|
||||
. /usr/share/openvswitch/scripts/ovs-lib || exit 1
|
||||
. /etc/xensource-inventory
|
||||
test -e /etc/sysconfig/openvswitch && . /etc/sysconfig/openvswitch
|
||||
|
||||
case `cat /etc/xensource/network.conf` in
|
||||
vswitch|openvswitch)
|
||||
;;
|
||||
bridge)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Open vSwitch disabled (/etc/xensource/network.conf is invalid)" >&2
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
start_ovs_xapi_sync () {
|
||||
if daemon_is_running ovs-xapi-sync; then
|
||||
log_success_msg "ovs-xapi-sync is already running"
|
||||
else
|
||||
PYTHONPATH=/usr/share/openvswitch/python \
|
||||
/usr/share/openvswitch/scripts/ovs-xapi-sync \
|
||||
--log-file --pidfile --detach --monitor unix:/var/run/openvswitch/db.sock
|
||||
fi
|
||||
}
|
||||
|
||||
start () {
|
||||
set ovs_ctl ${1-start}
|
||||
set "$@" --system-id="$INSTALLATION_UUID"
|
||||
set "$@" --system-type="$PRODUCT_BRAND"
|
||||
set "$@" --system-version="$PRODUCT_VERSION-$BUILD_NUMBER"
|
||||
set "$@" --external-id=xs-system-uuid="$INSTALLATION_UUID"
|
||||
set "$@" --daemon-cwd=/var/xen/openvswitch
|
||||
if test X"$FORCE_COREFILES" != X; then
|
||||
set "$@" --force-corefiles="$FORCE_COREFILES"
|
||||
fi
|
||||
if test X"$OVSDB_SERVER_PRIORITY" != X; then
|
||||
set "$@" --ovsdb-server-priority="$OVSDB_SERVER_PRIORITY"
|
||||
fi
|
||||
if test X"$VSWITCHD_PRIORITY" != X; then
|
||||
set "$@" --ovs-vswitchd-priority="$VSWITCHD_PRIORITY"
|
||||
fi
|
||||
if test X"$VSWITCHD_MLOCKALL" != X; then
|
||||
set "$@" --mlockall="$VSWITCHD_MLOCKALL"
|
||||
fi
|
||||
if test ! -e /var/run/openvswitch.booted; then
|
||||
touch /var/run/openvswitch.booted
|
||||
set "$@" --delete-bridges
|
||||
fi
|
||||
set "$@" $OVS_CTL_OPTS
|
||||
"$@"
|
||||
|
||||
start_ovs_xapi_sync
|
||||
|
||||
ovs_ctl --protocol=gre enable-protocol
|
||||
|
||||
touch /var/lock/subsys/openvswitch
|
||||
}
|
||||
|
||||
force_reload_kmod () {
|
||||
start force-reload-kmod
|
||||
|
||||
# Restart the high-availability daemon if it is running. Otherwise
|
||||
# it loses its heartbeat and reboots the system after a few minutes.
|
||||
if pidof xhad >/dev/null && test -e /etc/xensource/xhad.conf; then
|
||||
PATH=$PATH:/opt/xensource/xha
|
||||
action "Stopping HA daemon" ha_stop_daemon
|
||||
action "Starting HA daemon" ha_start_daemon
|
||||
fi
|
||||
|
||||
action "Stopping ovs-xapi-sync" stop_daemon ovs-xapi-sync
|
||||
action "Starting ovs-xapi-sync" start_ovs_xapi_sync
|
||||
}
|
||||
|
||||
stop () {
|
||||
ovs_ctl stop
|
||||
stop_daemon ovs-xapi-sync
|
||||
rm -f /var/lock/subsys/openvswitch
|
||||
}
|
||||
|
||||
restart () {
|
||||
if [ "$1" = "--save-flows=yes" ]; then
|
||||
stop_daemon ovs-xapi-sync
|
||||
start restart
|
||||
else
|
||||
stop
|
||||
start
|
||||
fi
|
||||
}
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
shift
|
||||
restart "$@"
|
||||
;;
|
||||
reload|force-reload)
|
||||
# The main OVS daemons keep up-to-date, but ovs-xapi-sync needs help.
|
||||
if daemon_is_running ovs-xapi-sync; then
|
||||
action "Configuring Open vSwitch external IDs" \
|
||||
ovs-appctl -t ovs-xapi-sync flush-cache
|
||||
fi
|
||||
;;
|
||||
status)
|
||||
ovs_ctl status && daemon_status ovs-xapi-sync
|
||||
;;
|
||||
version)
|
||||
ovs_ctl version
|
||||
;;
|
||||
force-reload-kmod)
|
||||
force_reload_kmod
|
||||
;;
|
||||
help)
|
||||
printf "openvswitch [start|stop|restart|reload|force-reload|status|version]\n"
|
||||
;;
|
||||
*)
|
||||
printf "Unknown command: $1\n"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
@@ -1,80 +0,0 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# openvswitch-xapi-update
|
||||
#
|
||||
# chkconfig: 2345 95 01
|
||||
# description: Update Open vSwitch configuration from XAPI database at boot
|
||||
|
||||
# Copyright (C) 2009, 2010 Nicira, 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.
|
||||
### BEGIN INIT INFO
|
||||
# Provides: openvswitch-xapi-update
|
||||
# Required-Start: $network $remote_fs
|
||||
# Required-Stop: $network
|
||||
# Default-Start: 3 5
|
||||
# Default-Stop:
|
||||
# Short-Description: openvswitch-xapi-update
|
||||
# Description: reconfigures Open vSwitch based on XAPI configuration
|
||||
### END INIT INFO
|
||||
|
||||
. /etc/init.d/functions
|
||||
|
||||
function do_host_call {
|
||||
xe host-call-plugin host-uuid="$INSTALLATION_UUID" plugin="openvswitch-cfg-update" fn="update" >/dev/null
|
||||
}
|
||||
|
||||
function start {
|
||||
if [ ! -f /etc/xensource-inventory ]; then
|
||||
printf "openvswitch-xapi-update ERROR: XenSource inventory not present in /etc/xensource-inventory\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if test -e /etc/xensource/network.conf; then
|
||||
NETWORK_MODE=$(cat /etc/xensource/network.conf)
|
||||
fi
|
||||
|
||||
case ${NETWORK_MODE:=openvswitch} in
|
||||
vswitch|openvswitch)
|
||||
;;
|
||||
bridge)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Open vSwitch disabled (/etc/xensource/network.conf is invalid)" >&2
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
source /etc/xensource-inventory
|
||||
action "Updating configuration" do_host_call
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
# Nothing to do here.
|
||||
;;
|
||||
restart)
|
||||
start
|
||||
;;
|
||||
help)
|
||||
printf "openvswitch-xapi-update [start|stop|restart]\n"
|
||||
;;
|
||||
*)
|
||||
printf "Unknown command: $1\n"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
@@ -1,21 +0,0 @@
|
||||
# Copyright (C) 2009, 2010, 2011, 2012, 2017 Nicira, Inc.
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without warranty of any kind.
|
||||
|
||||
/var/log/openvswitch/*.log {
|
||||
daily
|
||||
compress
|
||||
sharedscripts
|
||||
missingok
|
||||
postrotate
|
||||
# Tell Open vSwitch daemons to reopen their log files
|
||||
if [ -d /var/run/openvswitch ]; then
|
||||
for pidfile in `cd /var/run/openvswitch && echo *.pid`; do
|
||||
ovs-appctl -t "${pidfile%%.pid}" vlog/reopen 2>/dev/null || :
|
||||
done
|
||||
fi
|
||||
endscript
|
||||
}
|
@@ -1,48 +0,0 @@
|
||||
# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without warranty of any kind.
|
||||
|
||||
alias vswitch='service openvswitch'
|
||||
alias openvswitch='service openvswitch'
|
||||
|
||||
function watchdp {
|
||||
watch ovs-dpctl show "$@"
|
||||
}
|
||||
|
||||
function watchdpflows {
|
||||
local grep=""
|
||||
local dp=$1
|
||||
shift
|
||||
if [ $# -gt 0 ]; then
|
||||
grep="| grep $@"
|
||||
fi
|
||||
watch "ovs-dpctl dump-flows $dp $grep"
|
||||
}
|
||||
|
||||
function watchflows {
|
||||
local grep=""
|
||||
local dp=$1
|
||||
shift
|
||||
bridge=$(ovs-dpctl show $dp | grep 'port 0:' | cut -d' ' -f 3)
|
||||
if [ $# -gt 0 ]; then
|
||||
grep="| grep $@"
|
||||
fi
|
||||
watch "ovs-ofctl dump-flows unix:/var/run/$bridge.mgmt $grep"
|
||||
}
|
||||
|
||||
function monitorlogs {
|
||||
local grep=""
|
||||
if [ $# -gt 0 ]; then
|
||||
grep="| grep --line-buffered '^==> .* <==$"
|
||||
for i in "$@"; do
|
||||
grep="$grep\|$i"
|
||||
done
|
||||
grep="$grep'"
|
||||
fi
|
||||
cmd="tail -F /var/log/messages /var/log/openvswitch/ovs-vswitchd.log /var/log/openvswitch/ovsdb-server /var/log/xensource.log $grep | tee /var/log/monitorlogs.out"
|
||||
printf "cmd: $cmd\n"
|
||||
eval "$cmd"
|
||||
}
|
@@ -1,269 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# xapi plugin script to update the cache of configuration items in the
|
||||
# ovs-vswitchd configuration that are managed in the xapi database when
|
||||
# integrated with Citrix management tools.
|
||||
|
||||
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2020 Nicira, 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.
|
||||
|
||||
# TBD: - error handling needs to be improved. Currently this can leave
|
||||
# TBD: the system in a bad state if anything goes wrong.
|
||||
|
||||
import XenAPIPlugin
|
||||
import os
|
||||
import subprocess
|
||||
import syslog
|
||||
import re
|
||||
|
||||
vsctl = '/usr/bin/ovs-vsctl'
|
||||
ofctl = '/usr/bin/ovs-ofctl'
|
||||
cacert_filename = '/etc/openvswitch/vswitchd.cacert'
|
||||
ovsdb_port = '6640'
|
||||
|
||||
|
||||
# Delete the CA certificate, so that we go back to boot-strapping mode
|
||||
def delete_cacert():
|
||||
try:
|
||||
os.remove(cacert_filename)
|
||||
except OSError:
|
||||
# Ignore error if file doesn't exist
|
||||
pass
|
||||
|
||||
|
||||
def update(session, args):
|
||||
# Refresh bridge network UUIDs in case this host joined or left a pool.
|
||||
script = '/opt/xensource/libexec/interface-reconfigure'
|
||||
try:
|
||||
retval = subprocess.call([script, 'rewrite'])
|
||||
if retval != 0:
|
||||
syslog.syslog('%s exited with status %d' % (script, retval))
|
||||
except OSError, e:
|
||||
syslog.syslog('%s: failed to execute (%s)' % (script, e.strerror))
|
||||
|
||||
pools = session.xenapi.pool.get_all()
|
||||
# We assume there is only ever one pool...
|
||||
if len(pools) == 0:
|
||||
raise XenAPIPlugin.Failure('NO_POOL_FOR_HOST', [])
|
||||
if len(pools) > 1:
|
||||
raise XenAPIPlugin.Failure('MORE_THAN_ONE_POOL_FOR_HOST', [])
|
||||
new_controller = False
|
||||
pool = session.xenapi.pool.get_record(pools[0])
|
||||
controller = pool.get('vswitch_controller')
|
||||
ret_str = ''
|
||||
currentControllers = vswitchCurrentControllers()
|
||||
|
||||
if not controller and currentControllers:
|
||||
delete_cacert()
|
||||
try:
|
||||
emergency_reset(session, None)
|
||||
except:
|
||||
pass
|
||||
removeControllerCfg()
|
||||
ret_str += 'Successfully removed controller config. '
|
||||
# controller cannot be empty, otherwise, this will always be True.
|
||||
elif controller and controller not in currentControllers:
|
||||
delete_cacert()
|
||||
try:
|
||||
emergency_reset(session, None)
|
||||
except:
|
||||
pass
|
||||
setControllerCfg(controller)
|
||||
new_controller = True
|
||||
ret_str += 'Successfully set controller to %s. ' % controller
|
||||
|
||||
try:
|
||||
pool_fail_mode = pool['other_config']['vswitch-controller-fail-mode']
|
||||
except KeyError, e:
|
||||
pool_fail_mode = None
|
||||
|
||||
bton = {}
|
||||
|
||||
for rec in session.xenapi.network.get_all_records().values():
|
||||
try:
|
||||
bton[rec['bridge']] = rec
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# If new controller, get management MAC addresses from XAPI now
|
||||
# in case fail_mode set to secure which may affect XAPI access
|
||||
mgmt_bridge = None
|
||||
host_mgmt_mac = None
|
||||
host_mgmt_device = None
|
||||
pool_mgmt_macs = {}
|
||||
if new_controller:
|
||||
query = 'field "management"="true"'
|
||||
recs = session.xenapi.PIF.get_all_records_where(query)
|
||||
for rec in recs.itervalues():
|
||||
pool_mgmt_macs[rec.get('MAC')] = rec.get('device')
|
||||
|
||||
dib_changed = False
|
||||
fail_mode_changed = False
|
||||
for bridge in vswitchCfgQuery(['list-br']).split():
|
||||
network = bton[bridge]
|
||||
bridge = vswitchCfgQuery(['br-to-parent', bridge])
|
||||
|
||||
xapi_dib = network['other_config'].get('vswitch-disable-in-band')
|
||||
if not xapi_dib:
|
||||
xapi_dib = ''
|
||||
|
||||
ovs_dib = vswitchCfgQuery(['--', '--if-exists', 'get', 'Bridge',
|
||||
bridge,
|
||||
'other_config:disable-in-band']).strip('"')
|
||||
|
||||
# Do nothing if setting is invalid, and warn the user.
|
||||
if xapi_dib not in ['true', 'false', '']:
|
||||
ret_str += '"' + xapi_dib + '"' + \
|
||||
' is an invalid value for vswitch-disable-in-band on ' + \
|
||||
bridge + ' '
|
||||
|
||||
# Change bridge disable-in-band option if XAPI and OVS states differ.
|
||||
elif xapi_dib != ovs_dib:
|
||||
# 'true' or 'false'
|
||||
if xapi_dib:
|
||||
vswitchCfgMod(['--', 'set', 'Bridge', bridge,
|
||||
'other_config:disable-in-band=' + xapi_dib])
|
||||
# '' or None
|
||||
else:
|
||||
vswitchCfgMod(['--', 'remove', 'Bridge', bridge,
|
||||
'other_config', 'disable-in-band'])
|
||||
dib_changed = True
|
||||
|
||||
# Change bridge fail_mode if XAPI state differs from OVS state.
|
||||
bridge_fail_mode = vswitchCfgQuery(['get', 'Bridge',
|
||||
bridge, 'fail_mode']).strip('[]"')
|
||||
|
||||
try:
|
||||
other_config = bton[bridge]['other_config']
|
||||
fail_mode = other_config['vswitch-controller-fail-mode']
|
||||
except KeyError, e:
|
||||
fail_mode = None
|
||||
|
||||
if fail_mode not in ['secure', 'standalone']:
|
||||
fail_mode = pool_fail_mode
|
||||
|
||||
if fail_mode != 'secure':
|
||||
fail_mode = 'standalone'
|
||||
|
||||
if bridge_fail_mode != fail_mode:
|
||||
vswitchCfgMod(['--', 'set', 'Bridge', bridge,
|
||||
'fail_mode=%s' % fail_mode])
|
||||
fail_mode_changed = True
|
||||
|
||||
# Determine local mgmt MAC address if host being added to secure
|
||||
# pool so we can add default flows to allow management traffic
|
||||
if new_controller and fail_mode_changed and pool_fail_mode == 'secure':
|
||||
oc = vswitchCfgQuery(['get', 'Bridge', bridge, 'other-config'])
|
||||
m = re.match('.*hwaddr="([0-9a-fA-F:].*)".*', oc)
|
||||
if m and m.group(1) in pool_mgmt_macs.keys():
|
||||
mgmt_bridge = bridge
|
||||
host_mgmt_mac = m.group(1)
|
||||
host_mgmt_device = pool_mgmt_macs[host_mgmt_mac]
|
||||
|
||||
if (host_mgmt_mac is not None and mgmt_bridge is not None and
|
||||
host_mgmt_device is not None):
|
||||
tp = 'idle_timeout=0,priority=0'
|
||||
port = vswitchCfgQuery(['get', 'interface', host_mgmt_device,
|
||||
'ofport'])
|
||||
|
||||
addFlow(mgmt_bridge, '%s,in_port=%s,arp,nw_proto=1,actions=local' %
|
||||
(tp, port))
|
||||
addFlow(mgmt_bridge, '%s,in_port=local,arp,dl_src=%s,actions=%s' %
|
||||
(tp, host_mgmt_mac, port))
|
||||
addFlow(mgmt_bridge, '%s,in_port=%s,dl_dst=%s,actions=local' %
|
||||
(tp, port, host_mgmt_mac))
|
||||
addFlow(mgmt_bridge, '%s,in_port=local,dl_src=%s,actions=%s' %
|
||||
(tp, host_mgmt_mac, port))
|
||||
|
||||
if dib_changed:
|
||||
ret_str += 'Updated in-band management. '
|
||||
if fail_mode_changed:
|
||||
ret_str += 'Updated fail_mode. '
|
||||
|
||||
if ret_str != '':
|
||||
return ret_str
|
||||
else:
|
||||
return 'No change to configuration'
|
||||
|
||||
|
||||
def vswitchCurrentControllers():
|
||||
controllers = vswitchCfgQuery(['get-manager'])
|
||||
|
||||
def parse_controller(controller):
|
||||
if controller.startswith('ssl:'):
|
||||
return controller.split(':')[1]
|
||||
|
||||
return controller.split(':')[0]
|
||||
|
||||
return [parse_controller(controller)
|
||||
for controller in controllers.split('\n')
|
||||
if controller]
|
||||
|
||||
|
||||
def removeControllerCfg():
|
||||
vswitchCfgMod(['--', 'del-manager',
|
||||
'--', 'del-ssl'])
|
||||
|
||||
|
||||
def setControllerCfg(controller):
|
||||
# /etc/xensource/xapi-ssl.pem is mentioned twice below because it
|
||||
# contains both the private key and the certificate.
|
||||
vswitchCfgMod(['--', 'del-manager',
|
||||
'--', 'del-ssl',
|
||||
'--', '--bootstrap', 'set-ssl',
|
||||
'/etc/xensource/xapi-ssl.pem',
|
||||
'/etc/xensource/xapi-ssl.pem',
|
||||
cacert_filename,
|
||||
'--', 'set-manager', 'ssl:' + controller + ':' + ovsdb_port])
|
||||
|
||||
|
||||
def vswitchCfgQuery(action_args):
|
||||
cmd = [vsctl, '-vconsole:off'] + action_args
|
||||
output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
|
||||
if len(output) == 0 or output[0] is None:
|
||||
output = ''
|
||||
else:
|
||||
output = output[0].strip()
|
||||
return output
|
||||
|
||||
|
||||
def vswitchCfgMod(action_args):
|
||||
cmd = [vsctl, '--timeout=5', '-vconsole:off'] + action_args
|
||||
exitcode = subprocess.call(cmd)
|
||||
if exitcode != 0:
|
||||
raise XenAPIPlugin.Failure('VSWITCH_CONFIG_MOD_FAILURE',
|
||||
[str(exitcode), str(action_args)])
|
||||
|
||||
|
||||
def emergency_reset(session, args):
|
||||
cmd = [vsctl, '--timeout=5', 'emer-reset']
|
||||
exitcode = subprocess.call(cmd)
|
||||
if exitcode != 0:
|
||||
raise XenAPIPlugin.Failure('VSWITCH_EMER_RESET_FAILURE',
|
||||
[str(exitcode)])
|
||||
|
||||
return 'Successfully reset configuration'
|
||||
|
||||
|
||||
def addFlow(switch, flow):
|
||||
cmd = [ofctl, 'add-flow', switch, flow]
|
||||
exitcode = subprocess.call(cmd)
|
||||
if exitcode != 0:
|
||||
raise XenAPIPlugin.Failure('VSWITCH_ADD_FLOW_FAILURE',
|
||||
[str(exitcode), str(switch), str(flow)])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
XenAPIPlugin.dispatch({'update': update,
|
||||
'emergency_reset': emergency_reset})
|
@@ -1,265 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Copyright (C) 2008,2009 Citrix Systems, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation; version 2.1 only. with the special
|
||||
# exception on linking described in file LICENSE.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
|
||||
# CA-23900: Warning: when VIFs are added to windows guests with PV drivers the backend vif device is registered,
|
||||
# unregistered and then registered again. This causes the udev event to fire twice and this script runs twice.
|
||||
# Since the first invocation of the script races with the device unregistration, spurious errors are possible
|
||||
# which will be logged but are safe to ignore since the second script invocation should complete the operation.
|
||||
# Note that each script invocation is run synchronously from udev and so the scripts don't race with each other.
|
||||
|
||||
# Keep other-config/ keys in sync with device.ml:vif_udev_keys
|
||||
|
||||
BRCTL="/usr/sbin/brctl"
|
||||
IP="/sbin/ip"
|
||||
|
||||
vsctl="/usr/bin/ovs-vsctl"
|
||||
|
||||
handle_promiscuous()
|
||||
{
|
||||
local arg=$(xenstore-read "${PRIVATE}/other-config/promiscuous" 2>/dev/null)
|
||||
if [ $? -eq 0 -a -n "${arg}" ] ; then
|
||||
case $NETWORK_MODE in
|
||||
bridge)
|
||||
case "${arg}" in
|
||||
true|on) echo 1 > /sys/class/net/${dev}/brport/promisc ;;
|
||||
*) echo 0 > /sys/class/net/${dev}/brport/promisc ;;
|
||||
esac
|
||||
;;
|
||||
openvswitch)
|
||||
logger -t script-vif "${dev}: Promiscuous ports are not supported via Open vSwitch."
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
handle_ethtool()
|
||||
{
|
||||
local opt=$1
|
||||
local arg=$(xenstore-read "${PRIVATE}/other-config/ethtool-${opt}" 2>/dev/null)
|
||||
if [ $? -eq 0 -a -n "${arg}" ] ; then
|
||||
case "${arg}" in
|
||||
true|on) /sbin/ethtool -K "${dev}" "${opt}" on ;;
|
||||
false|off) /sbin/ethtool -K "${dev}" "${opt}" off ;;
|
||||
*) logger -t scripts-vif "Unknown ethtool argument ${opt}=${arg} on ${dev}/${VIFUUID}" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
handle_mtu()
|
||||
{
|
||||
local mtu=$(xenstore-read "${PRIVATE}/MTU" 2>/dev/null)
|
||||
if [ $? -eq 0 -a -n "${mtu}" ]; then
|
||||
logger -t scripts-vif "Setting ${dev} MTU ${mtu}"
|
||||
${IP} link set "${dev}" mtu ${mtu} || logger -t scripts-vif "Failed to ip link set ${dev} mtu ${mtu}. Error code $?"
|
||||
fi
|
||||
}
|
||||
|
||||
set_vif_external_id()
|
||||
{
|
||||
local key=$1
|
||||
local value=$2
|
||||
|
||||
logger -t scripts-vif "vif${DOMID}.${DEVID} external-ids:\"${key}\"=\"${value}\""
|
||||
|
||||
echo "-- set interface vif${DOMID}.${DEVID} external-ids:\"${key}\"=\"${value}\""
|
||||
}
|
||||
|
||||
handle_vswitch_vif_details()
|
||||
{
|
||||
local vm=$(xenstore-read "/local/domain/$DOMID/vm" 2>/dev/null)
|
||||
if [ $? -eq 0 -a -n "${vm}" ] ; then
|
||||
local vm_uuid=$(xenstore-read "$vm/uuid" 2>/dev/null)
|
||||
fi
|
||||
if [ -n "${vm_uuid}" ] ; then
|
||||
set_vif_external_id "xs-vm-uuid" "${vm_uuid}"
|
||||
fi
|
||||
|
||||
local vif_uuid=$(xenstore-read "${PRIVATE}/vif-uuid" 2>/dev/null)
|
||||
if [ -n "${vif_uuid}" ] ; then
|
||||
set_vif_external_id "xs-vif-uuid" "${vif_uuid}"
|
||||
fi
|
||||
|
||||
local vif_details=
|
||||
local net_uuid=$(xenstore-read "${PRIVATE}/network-uuid" 2>/dev/null)
|
||||
if [ -n "${net_uuid}" ] ; then
|
||||
set_vif_external_id "xs-network-uuid" "${net_uuid}"
|
||||
fi
|
||||
local address=$(xenstore-read "/local/domain/$DOMID/device/vif/$DEVID/mac" 2>/dev/null)
|
||||
if [ -n "${address}" ] ; then
|
||||
set_vif_external_id "attached-mac" "${address}"
|
||||
fi
|
||||
}
|
||||
|
||||
add_to_bridge()
|
||||
{
|
||||
local address=$(xenstore-read "${PRIVATE}/bridge-MAC")
|
||||
if [ $? -ne 0 -o -z "${address}" ]; then
|
||||
logger -t scripts-vif "Failed to read ${PRIVATE}/bridge-MAC from xenstore"
|
||||
exit 1
|
||||
fi
|
||||
local bridge=$(xenstore-read "${PRIVATE}/bridge")
|
||||
if [ $? -ne 0 -o -z "${bridge}" ]; then
|
||||
logger -t scripts-vif "Failed to read ${PRIVATE}/bridge from xenstore"
|
||||
exit 1
|
||||
fi
|
||||
logger -t scripts-vif "Adding ${dev} to ${bridge} with address ${address}"
|
||||
|
||||
${IP} link set "${dev}" down || logger -t scripts-vif "Failed to ip link set ${dev} down"
|
||||
${IP} link set "${dev}" arp off || logger -t scripts-vif "Failed to ip link set ${dev} arp off"
|
||||
${IP} link set "${dev}" multicast off || logger -t scripts-vif "Failed to ip link set ${dev} multicast off"
|
||||
${IP} link set "${dev}" address "${address}" || logger -t scripts-vif "Failed to ip link set ${dev} address ${address}"
|
||||
${IP} addr flush "${dev}" || logger -t scripts-vif "Failed to ip addr flush ${dev}"
|
||||
|
||||
case $NETWORK_MODE in
|
||||
bridge)
|
||||
${BRCTL} setfd "${bridge}" 0 || logger -t scripts-vif "Failed to brctl setfd ${bridge} 0"
|
||||
${BRCTL} addif "${bridge}" "${dev}" || logger -t scripts-vif "Failed to brctl addif ${bridge} ${dev}"
|
||||
;;
|
||||
openvswitch)
|
||||
if [ "$TYPE" = "vif" ] ; then
|
||||
local vif_details=$(handle_vswitch_vif_details $bridge)
|
||||
fi
|
||||
|
||||
$vsctl --timeout=30 -- --if-exists del-port $dev -- add-port $bridge $dev $vif_details
|
||||
;;
|
||||
esac
|
||||
|
||||
${IP} link set "${dev}" up || logger -t scripts-vif "Failed to ip link set ${dev} up"
|
||||
}
|
||||
|
||||
remove_from_bridge()
|
||||
{
|
||||
case $NETWORK_MODE in
|
||||
bridge)
|
||||
# Nothing to do
|
||||
;;
|
||||
openvswitch)
|
||||
$vsctl --timeout=30 -- del-port $dev
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
call_hook_script() {
|
||||
local domid=$1
|
||||
local action=$2
|
||||
# Call the VIF hotplug hook if present
|
||||
if [ -x /etc/xapi.d/vif-hotplug ]; then
|
||||
local vm=$(xenstore-read "/local/domain/$domid/vm" 2>/dev/null)
|
||||
if [ $? -eq 0 -a -n "${vm}" ] ; then
|
||||
local vm_uuid=$(xenstore-read "$vm/uuid" 2>/dev/null)
|
||||
fi
|
||||
if [ -n "${vm_uuid}" ] ; then
|
||||
logger -t scripts-vif "VM UUID ${vm_uuid}"
|
||||
fi
|
||||
|
||||
local vif_uuid=$(xenstore-read "${PRIVATE}/vif-uuid" 2>/dev/null)
|
||||
if [ -n "${vif_uuid}" ] ; then
|
||||
logger -t scripts-vif "VIF UUID ${vif_uuid}"
|
||||
fi
|
||||
if [ -n "${vif_uuid}" -a -n "${vm_uuid}" ] ; then
|
||||
logger -t scripts-vif "Calling VIF hotplug hook for VM ${vm_uuid}, VIF ${vif_uuid}"
|
||||
/etc/xapi.d/vif-hotplug -action "${action}" -vifuuid "${vif_uuid}" -vmuuid "${vm_uuid}"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
NETWORK_MODE=$(cat /etc/xensource/network.conf)
|
||||
ACTION=$1
|
||||
|
||||
# Older versions of XenServer do not pass in the type as an argument
|
||||
if [[ $# -lt 2 ]]; then
|
||||
TYPE=vif
|
||||
else
|
||||
TYPE=$2
|
||||
fi
|
||||
|
||||
case $NETWORK_MODE in
|
||||
bridge|openvswitch) ;;
|
||||
vswitch) NETWORK_MODE=openvswitch ;;
|
||||
*)
|
||||
logger -t scripts-vif "Unknown network mode $NETWORK_MODE"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
case ${TYPE} in
|
||||
vif)
|
||||
if [ -z ${XENBUS_PATH} ]; then
|
||||
DOMID=$3
|
||||
DEVID=$4
|
||||
else
|
||||
DOMID=`echo ${XENBUS_PATH} | cut -f 3 -d '/'`
|
||||
DEVID=`echo ${XENBUS_PATH} | cut -f 4 -d '/'`
|
||||
fi
|
||||
dev=vif${DOMID}.${DEVID}
|
||||
;;
|
||||
tap)
|
||||
dev=$INTERFACE
|
||||
DOMID=`echo ${dev#tap} | cut -f 1 -d '.'`
|
||||
DEVID=`echo ${dev#tap} | cut -f 2 -d '.'`
|
||||
;;
|
||||
*)
|
||||
logger -t scripts-vif "unknown interface type ${TYPE}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
XAPI=/xapi/${DOMID}/hotplug/vif/${DEVID}
|
||||
HOTPLUG=/xapi/${DOMID}/hotplug/vif/${DEVID}
|
||||
PRIVATE=/xapi/${DOMID}/private/vif/${DEVID}
|
||||
|
||||
logger -t scripts-vif "Called as \"$@\" domid:$DOMID devid:$DEVID mode:$NETWORK_MODE"
|
||||
case "${ACTION}" in
|
||||
online)
|
||||
if [ "${TYPE}" = "vif" ] ; then
|
||||
handle_ethtool rx
|
||||
handle_ethtool tx
|
||||
handle_ethtool sg
|
||||
handle_ethtool tso
|
||||
handle_ethtool ufo
|
||||
handle_ethtool gso
|
||||
|
||||
handle_mtu
|
||||
add_to_bridge
|
||||
handle_promiscuous
|
||||
|
||||
xenstore-write "${HOTPLUG}/vif" "${dev}"
|
||||
xenstore-write "${HOTPLUG}/hotplug" "online"
|
||||
|
||||
# xs-xen.pq.hq:91e986b8e49f netback-wait-for-hotplug
|
||||
xenstore-write "/local/domain/0/backend/vif/${DOMID}/${DEVID}/hotplug-status" "connected"
|
||||
call_hook_script $DOMID "${ACTION}"
|
||||
fi
|
||||
;;
|
||||
|
||||
add)
|
||||
if [ "${TYPE}" = "tap" ] ; then
|
||||
add_to_bridge
|
||||
fi
|
||||
;;
|
||||
|
||||
remove)
|
||||
if [ "${TYPE}" = "vif" ] ;then
|
||||
xenstore-rm "${HOTPLUG}/hotplug"
|
||||
call_hook_script $DOMID "${ACTION}"
|
||||
fi
|
||||
logger -t scripts-vif "${dev} has been removed"
|
||||
remove_from_bridge
|
||||
;;
|
||||
|
||||
move)
|
||||
if [ "${TYPE}" = "vif" ] ;then
|
||||
add_to_bridge
|
||||
fi
|
||||
esac
|
@@ -1,512 +0,0 @@
|
||||
# Spec file for Open vSwitch.
|
||||
|
||||
# Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification,
|
||||
# are permitted in any medium without royalty provided the copyright
|
||||
# notice and this notice are preserved. This file is offered as-is,
|
||||
# without warranty of any kind.
|
||||
|
||||
# For XenServer version < 6.5, when building, the rpmbuild command line
|
||||
# should define openvswitch_version, kernel_name, kernel_version and
|
||||
# kernel_flavor using -D arguments.
|
||||
# for example:
|
||||
#
|
||||
# rpmbuild -D "openvswitch_version 1.1.0+build123"
|
||||
# -D "kernel_name NAME-xen"
|
||||
# -D "kernel_version 2.6.32.12-0.7.1.xs5.6.100.323.170596"
|
||||
# -D "kernel_flavor xen"
|
||||
# -bb /usr/src/redhat/SPECS/openvswitch-xen.spec
|
||||
#
|
||||
# For XenServer version >= 6.5, use kernel_uname which should be
|
||||
# the `uname -r` output.
|
||||
# for example:
|
||||
#
|
||||
# rpmbuild -D "openvswitch_version 2.3.0+build123"
|
||||
# -D "kernel_uname 3.10.0+2"
|
||||
# -bb /usr/src/redhat/SPECS/openvswitch-xen.spec
|
||||
#
|
||||
# If tests have to be skipped while building, specify the '--without check'
|
||||
# option. For example:
|
||||
# rpmbuild -bb --without check xenserver/openvswitch-xen.spec
|
||||
|
||||
%if %{?openvswitch_version:0}%{!?openvswitch_version:1}
|
||||
%define openvswitch_version @VERSION@
|
||||
%endif
|
||||
|
||||
%if %{?kernel_uname:1}%{!?kernel_uname:0}
|
||||
%define kernel_name kernel
|
||||
%define kernel_version %{kernel_uname}
|
||||
%endif
|
||||
|
||||
%if %{?kernel_name:0}%{!?kernel_name:1}
|
||||
%define kernel %(rpm -qa 'kernel*xen-devel' | head -1)
|
||||
%define kernel_name %(rpm -q --queryformat "%%{Name}" %{kernel} | sed 's/-devel//' | sed 's/kernel-//')
|
||||
%define kernel_version %(rpm -q --queryformat "%%{Version}-%%{Release}" %{kernel})
|
||||
%define kernel_flavor xen
|
||||
%endif
|
||||
|
||||
%if %{?xen_version:0}%{!?xen_version:1}
|
||||
%define xen_version %{kernel_version}%{?kernel_flavor:%{kernel_flavor}}
|
||||
%endif
|
||||
|
||||
# bump this when breaking compatibility with userspace
|
||||
%define module_abi_version 0
|
||||
|
||||
# build-supplemental-pack.sh requires this naming for kernel module packages
|
||||
%define module_package modules%{?kernel_flavor:-%{kernel_flavor}}-%{kernel_version}
|
||||
|
||||
%bcond_without check
|
||||
|
||||
Name: openvswitch
|
||||
Summary: Open vSwitch daemon/database/utilities
|
||||
Group: System Environment/Daemons
|
||||
URL: http://www.openvswitch.org/
|
||||
Vendor: Nicira, Inc.
|
||||
Version: %{openvswitch_version}
|
||||
|
||||
License: ASL 2.0
|
||||
Release: 1
|
||||
Source: openvswitch-%{openvswitch_version}.tar.gz
|
||||
Buildroot: /tmp/openvswitch-xen-rpm
|
||||
Requires: openvswitch.ko.%{module_abi_version}
|
||||
|
||||
%description
|
||||
Open vSwitch provides standard network bridging functions augmented with
|
||||
support for the OpenFlow protocol for remote per-flow control of
|
||||
traffic.
|
||||
|
||||
%package %{module_package}
|
||||
Summary: Open vSwitch kernel module
|
||||
Group: System Environment/Kernel
|
||||
License: GPLv2
|
||||
Provides: %{name}-modules%{?kernel_flavor:-%{kernel_flavor}} = %{kernel_version}, openvswitch.ko.%{module_abi_version}
|
||||
%if %{?kernel_uname:0}%{!?kernel_uname:1}
|
||||
Requires: kernel%{?kernel_flavor:-%{kernel_flavor}} = %{kernel_version}
|
||||
%endif
|
||||
%if %{?kernel_uname:1}%{!?kernel_uname:0}
|
||||
Requires: kernel-uname-r = %{kernel_version}
|
||||
%endif
|
||||
|
||||
%description %{module_package}
|
||||
Open vSwitch Linux kernel module compiled against kernel version
|
||||
%{kernel_version}%{?kernel_flavor:%{kernel_flavor}}.
|
||||
|
||||
%prep
|
||||
%setup -q -n openvswitch-%{openvswitch_version}
|
||||
|
||||
%build
|
||||
./configure --prefix=/usr --sysconfdir=/etc --localstatedir=%{_localstatedir} --with-linux=/lib/modules/%{xen_version}/build --enable-ssl CFLAGS='-g -O2 -msse -msse2'
|
||||
make %{_smp_mflags}
|
||||
|
||||
%install
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
make install DESTDIR=$RPM_BUILD_ROOT
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc/init.d
|
||||
install -m 755 xenserver/etc_init.d_openvswitch \
|
||||
$RPM_BUILD_ROOT/etc/init.d/openvswitch
|
||||
install -m 755 xenserver/etc_init.d_openvswitch-xapi-update \
|
||||
$RPM_BUILD_ROOT/etc/init.d/openvswitch-xapi-update
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc/sysconfig
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc/logrotate.d
|
||||
install -m 755 xenserver/etc_logrotate.d_openvswitch \
|
||||
$RPM_BUILD_ROOT/etc/logrotate.d/openvswitch
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc/profile.d
|
||||
install -m 755 xenserver/etc_profile.d_openvswitch.sh \
|
||||
$RPM_BUILD_ROOT/etc/profile.d/openvswitch.sh
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc/xapi.d/plugins
|
||||
install -m 755 xenserver/etc_xapi.d_plugins_openvswitch-cfg-update \
|
||||
$RPM_BUILD_ROOT/etc/xapi.d/plugins/openvswitch-cfg-update
|
||||
install -d -m 755 $RPM_BUILD_ROOT/usr/share/openvswitch/scripts
|
||||
install -m 755 xenserver/opt_xensource_libexec_interface-reconfigure \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/interface-reconfigure
|
||||
install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigure.py \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigure.py
|
||||
install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigureBridge.py \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigureBridge.py
|
||||
install -m 644 xenserver/opt_xensource_libexec_InterfaceReconfigureVswitch.py \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/InterfaceReconfigureVswitch.py
|
||||
install -m 755 xenserver/etc_xensource_scripts_vif \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/vif
|
||||
install -m 755 xenserver/usr_share_openvswitch_scripts_ovs-xapi-sync \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/ovs-xapi-sync
|
||||
install -m 755 xenserver/usr_share_openvswitch_scripts_sysconfig.template \
|
||||
$RPM_BUILD_ROOT/usr/share/openvswitch/scripts/sysconfig.template
|
||||
install -d -m 755 $RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base
|
||||
install -m 644 \
|
||||
xenserver/usr_lib_xsconsole_plugins-base_XSFeatureVSwitch.py \
|
||||
$RPM_BUILD_ROOT/usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py
|
||||
|
||||
install -d -m 755 $RPM_BUILD_ROOT/lib/modules/%{xen_version}/extra/openvswitch
|
||||
find datapath/linux -name *.ko -exec install -m 755 \{\} $RPM_BUILD_ROOT/lib/modules/%{xen_version}/extra/openvswitch \;
|
||||
|
||||
install -d -m 755 $RPM_BUILD_ROOT/etc/xensource/bugtool
|
||||
cp -rf $RPM_BUILD_ROOT/usr/share/openvswitch/bugtool-plugins/* $RPM_BUILD_ROOT/etc/xensource/bugtool
|
||||
|
||||
# Get rid of stuff we don't want to make RPM happy.
|
||||
rm \
|
||||
$RPM_BUILD_ROOT/usr/bin/ovs-testcontroller \
|
||||
$RPM_BUILD_ROOT/usr/bin/ovs-l3ping \
|
||||
$RPM_BUILD_ROOT/usr/bin/ovs-pki \
|
||||
$RPM_BUILD_ROOT/usr/bin/ovs-test \
|
||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-testcontroller.8 \
|
||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-l3ping.8 \
|
||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-pki.8 \
|
||||
$RPM_BUILD_ROOT/usr/share/man/man8/ovs-test.8
|
||||
(cd "$RPM_BUILD_ROOT" && rm -f usr/lib/lib*)
|
||||
(cd "$RPM_BUILD_ROOT" && rm -rf usr/include)
|
||||
(cd "$RPM_BUILD_ROOT" && rm -rf usr/lib/pkgconfig)
|
||||
|
||||
install -d -m 755 $RPM_BUILD_ROOT/var/lib/openvswitch
|
||||
|
||||
%check
|
||||
%if %{with check}
|
||||
if make check TESTSUITEFLAGS='%{_smp_mflags}' RECHECK=yes; then :;
|
||||
else
|
||||
cat tests/testsuite.log
|
||||
exit 1
|
||||
fi
|
||||
%endif
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
%post
|
||||
# A list of Citrix XenServer scripts that we might need to replace
|
||||
# with our own versions.
|
||||
scripts="
|
||||
/etc/xensource/scripts/vif
|
||||
/opt/xensource/libexec/InterfaceReconfigure.py
|
||||
/opt/xensource/libexec/InterfaceReconfigureBridge.py
|
||||
/opt/xensource/libexec/InterfaceReconfigureVswitch.py
|
||||
/opt/xensource/libexec/interface-reconfigure"
|
||||
|
||||
# Calculate into $md5sums a comma-separated set of md5sums of the
|
||||
# Citrix XenServer scripts that we might need to replace. We might be
|
||||
# upgrading an older version of the package that moved the files out
|
||||
# of the way, so we need to look for the files in those out-of-the-way
|
||||
# locations first.
|
||||
md5sums=
|
||||
for script in $scripts; do
|
||||
b=$(basename "$script")
|
||||
if test -e /usr/lib/openvswitch/xs-saved/"$b"; then
|
||||
f=/usr/lib/openvswitch/xs-saved/"$b"
|
||||
elif test -e /usr/lib/openvswitch/xs-original/"$b"; then
|
||||
f=/usr/lib/openvswitch/xs-original/"$b"
|
||||
elif test -e "$script" && test ! -h "$script"; then
|
||||
f=$script
|
||||
else
|
||||
printf "\n$script: not found\n"
|
||||
f=/dev/null
|
||||
fi
|
||||
md5sums="$md5sums,$(md5sum $f | awk '{print $1}')"
|
||||
done
|
||||
md5sums=${md5sums#,}
|
||||
|
||||
# Now check the md5sums against the known sets of md5sums:
|
||||
#
|
||||
# - If they are known to be a version of XenServer scripts that we should
|
||||
# replace, we replace them (by putting $scripts into $replace_files).
|
||||
#
|
||||
# - Otherwise, we guess that it's better not to replace them, because the
|
||||
# improvements that our versions of the scripts provide are minimal, so
|
||||
# it's better to avoid possibly breaking any changes made upstream by
|
||||
# Citrix.
|
||||
case $md5sums in
|
||||
cf09a68d9f8b434e79a4c83b01a3bb4b,395866df1b0b20c12c4dd2f7de0ecdb4,9d493545ae81463239d3162cbc798852,862d0939b441de9264a900628e950fe9,21f85db25599d7f026cd489385d58aa6)
|
||||
keep_files=
|
||||
replace_files=$scripts
|
||||
printf "\nVerified host scripts from XenServer 6.0.0.\n"
|
||||
;;
|
||||
|
||||
c5f48246577a17cf1b971fb5ce4e920b,2e2c912f86f9c536c89adc34ff3c2b2b,28d3ff72d72bdec4f37d70699f5edb76,67e1d0af16fc1ddf10009c5c063ad2ba,f3feff30aa3b3f8b514664a96a8dc0ab)
|
||||
keep_files=
|
||||
replace_files=$scripts
|
||||
printf "\nVerified host scripts from XenServer 5.6-SP2.\n"
|
||||
;;
|
||||
|
||||
c5f48246577a17cf1b971fb5ce4e920b,2e2c912f86f9c536c89adc34ff3c2b2b,28d3ff72d72bdec4f37d70699f5edb76,67e1d0af16fc1ddf10009c5c063ad2ba,24bae6906d182ba47668174f8e480cc6)
|
||||
keep_files=
|
||||
replace_files=$scripts
|
||||
printf "\nVerified host scripts from XenServer 5.6-FP1.\n"
|
||||
;;
|
||||
|
||||
*)
|
||||
keep_files=$scripts
|
||||
replace_files=
|
||||
cat <<EOF
|
||||
|
||||
The host scripts on this machine are not those of any supported
|
||||
version of XenServer. On XenServer earlier than 5.6-FP1, your Open
|
||||
vSwitch installation will not work. On XenServer 5.6-FP1 or later,
|
||||
Open vSwitch is not verified to work, which could lead to unexpected
|
||||
behavior.
|
||||
|
||||
EOF
|
||||
;;
|
||||
esac
|
||||
|
||||
if grep -F net.ipv4.conf.all.arp_filter /etc/sysctl.conf >/dev/null 2>&1; then :; else
|
||||
cat >>/etc/sysctl.conf <<EOF
|
||||
# This works around an issue in xhad, which binds to a particular
|
||||
# Ethernet device, which in turn causes ICMP port unreachable messages
|
||||
# if packets are received are on the wrong interface, which in turn
|
||||
# can happen if we send out ARP replies on every interface (as Linux
|
||||
# does by default) instead of just on the interface that has the IP
|
||||
# address being ARPed for, which this sysctl setting in turn works
|
||||
# around.
|
||||
#
|
||||
# Bug #1378.
|
||||
net.ipv4.conf.all.arp_filter = 1
|
||||
EOF
|
||||
fi
|
||||
|
||||
if test ! -e /etc/openvswitch/conf.db; then
|
||||
install -d -m 755 -o root -g root /etc/openvswitch
|
||||
|
||||
# Create ovs-vswitchd config database
|
||||
ovsdb-tool -vconsole:off create /etc/openvswitch/conf.db \
|
||||
/usr/share/openvswitch/vswitch.ovsschema
|
||||
|
||||
# Create initial table in config database
|
||||
ovsdb-tool -vconsole:off transact /etc/openvswitch/conf.db \
|
||||
'[{"op": "insert", "table": "Open_vSwitch", "row": {}}]' \
|
||||
> /dev/null
|
||||
fi
|
||||
|
||||
# Create default or update existing /etc/sysconfig/openvswitch.
|
||||
SYSCONFIG=/etc/sysconfig/openvswitch
|
||||
TEMPLATE=/usr/share/openvswitch/scripts/sysconfig.template
|
||||
if [ ! -e $SYSCONFIG ]; then
|
||||
cp $TEMPLATE $SYSCONFIG
|
||||
else
|
||||
for var in $(awk -F'[ :]' '/^# [_A-Z0-9]+:/{print $2}' $TEMPLATE)
|
||||
do
|
||||
if ! grep $var $SYSCONFIG >/dev/null 2>&1; then
|
||||
echo >> $SYSCONFIG
|
||||
sed -n "/$var:/,/$var=/p" $TEMPLATE >> $SYSCONFIG
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Deliberately break %postun in broken OVS builds that revert original
|
||||
# XenServer scripts during rpm -U by moving the directory where it thinks
|
||||
# they are saved.
|
||||
if [ -d /usr/lib/openvswitch/xs-original ]; then
|
||||
mkdir -p /usr/lib/openvswitch/xs-saved
|
||||
mv /usr/lib/openvswitch/xs-original/* /usr/lib/openvswitch/xs-saved/ &&
|
||||
rmdir /usr/lib/openvswitch/xs-original
|
||||
fi
|
||||
|
||||
# Replace XenServer files by our versions.
|
||||
mkdir -p /usr/lib/openvswitch/xs-saved \
|
||||
|| printf "Could not create script backup directory.\n"
|
||||
for f in $replace_files; do
|
||||
s=$(basename "$f")
|
||||
t=$(readlink "$f")
|
||||
if [ -f "$f" ] && [ "$t" != "/usr/share/openvswitch/scripts/$s" ]; then
|
||||
mv "$f" /usr/lib/openvswitch/xs-saved/ \
|
||||
|| printf "Could not save original XenServer $s script\n"
|
||||
ln -s "/usr/share/openvswitch/scripts/$s" "$f" \
|
||||
|| printf "Could not link to Open vSwitch $s script\n"
|
||||
fi
|
||||
done
|
||||
|
||||
# Clean up dangling symlinks to removed OVS replacement scripts no longer
|
||||
# provided by OVS. Any time a replacement script is removed from OVS,
|
||||
# it should be added here to ensure correct reversion from old versions of
|
||||
# OVS that don't clean up dangling symlinks during the uninstall phase.
|
||||
for orig in /usr/sbin/xen-bugtool $keep_files; do
|
||||
saved=/usr/lib/openvswitch/xs-saved/$(basename "$orig")
|
||||
[ -e "$saved" ] && mv -f "$saved" "$orig"
|
||||
done
|
||||
|
||||
# Ensure all required services are set to run
|
||||
for s in openvswitch openvswitch-xapi-update; do
|
||||
if chkconfig --list $s >/dev/null 2>&1; then
|
||||
chkconfig --del $s || printf "Could not remove $s init script.\n"
|
||||
fi
|
||||
chkconfig --add $s || printf "Could not add $s init script.\n"
|
||||
chkconfig $s on || printf "Could not enable $s init script.\n"
|
||||
done
|
||||
|
||||
if [ "$1" = "1" ]; then # $1 = 1 for install
|
||||
# Configure system to use Open vSwitch
|
||||
/opt/xensource/bin/xe-switch-network-backend vswitch
|
||||
else # $1 = 2 for upgrade
|
||||
|
||||
mode=$(cat /etc/xensource/network.conf)
|
||||
if [ "$mode" != "vswitch" ] && [ "$mode" != "openvswitch" ]; then
|
||||
printf "\nThe server is not configured to run Open vSwitch. To run in\n"
|
||||
printf "vswitch mode, you must run the following command:\n\n"
|
||||
printf " xe-switch-network-backend vswitch"
|
||||
printf "\n\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
%posttrans %{module_package}
|
||||
# Ensure that modprobe will find our modules.
|
||||
#
|
||||
# This has to be in %posttrans instead of %post because older versions
|
||||
# installed modules into a different directory and "rpm -U" runs the
|
||||
# new version's %post before removing the old version's files, so if
|
||||
# we use %post then depmod may find the old versions that are about to
|
||||
# be removed.
|
||||
depmod %{xen_version}
|
||||
|
||||
mode=$(cat /etc/xensource/network.conf)
|
||||
if [ "$mode" = "vswitch" ] || [ "$mode" = "openvswitch" ]; then
|
||||
printf "\nTo use the newly installed Open vSwitch kernel module, you\n"
|
||||
printf "will either have to reboot the hypervisor or follow any\n"
|
||||
printf "workarounds provided by your administration guide. Failure to do\n"
|
||||
printf "so may result in incorrect operation."
|
||||
printf "\n\n"
|
||||
fi
|
||||
|
||||
%preun
|
||||
if [ "$1" = "0" ]; then # $1 = 0 for uninstall
|
||||
# Configure system to use bridge
|
||||
/opt/xensource/bin/xe-switch-network-backend bridge
|
||||
|
||||
# The "openvswitch" service should have been removed from
|
||||
# "xe-switch-network-backend bridge".
|
||||
for s in openvswitch openvswitch-xapi-update; do
|
||||
if chkconfig --list $s >/dev/null 2>&1; then
|
||||
chkconfig --del $s || printf "Could not remove $s init script."
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
%postun
|
||||
# Restore original XenServer scripts if the OVS equivalent no longer exists.
|
||||
# This works both in the upgrade and erase cases.
|
||||
# This lists every file that every version of OVS has ever replaced. Never
|
||||
# remove old files that OVS no longer replaces, or upgrades from old versions
|
||||
# will fail to restore the XS originals, leaving the system in a broken state.
|
||||
# Also be sure to add removed script paths to the %post scriptlet above to
|
||||
# prevent the same problem when upgrading from old versions of OVS that lack
|
||||
# this restore-on-upgrade logic.
|
||||
for f in \
|
||||
/etc/xensource/scripts/vif \
|
||||
/usr/sbin/xen-bugtool \
|
||||
/opt/xensource/libexec/interface-reconfigure \
|
||||
/opt/xensource/libexec/InterfaceReconfigure.py \
|
||||
/opt/xensource/libexec/InterfaceReconfigureBridge.py \
|
||||
/opt/xensource/libexec/InterfaceReconfigureVswitch.py
|
||||
do
|
||||
# Only revert dangling symlinks.
|
||||
if [ -h "$f" ] && [ ! -e "$f" ]; then
|
||||
s=$(basename "$f")
|
||||
if [ ! -f "/usr/lib/openvswitch/xs-saved/$s" ]; then
|
||||
printf "Original XenServer $s script not present in /usr/lib/openvswitch/xs-saved\n" >&2
|
||||
printf "Could not restore original XenServer script.\n" >&2
|
||||
else
|
||||
(rm -f "$f" \
|
||||
&& mv "/usr/lib/openvswitch/xs-saved/$s" "$f") \
|
||||
|| printf "Could not restore original XenServer $s script.\n" >&2
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$1" = "0" ]; then # $1 = 0 for uninstall
|
||||
rm -f /usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.pyc \
|
||||
/usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.pyo
|
||||
|
||||
rm -f /usr/share/openvswitch/scripts/InterfaceReconfigure.pyc \
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigure.pyo \
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigureBridge.pyc \
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigureBridge.pyo \
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigureVSwitch.pyc \
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigureVSwitch.pyo
|
||||
|
||||
# Remove all configuration files
|
||||
rm -f /etc/openvswitch/conf.db
|
||||
rm -f /etc/sysconfig/openvswitch
|
||||
rm -f /etc/openvswitch/vswitchd.cacert
|
||||
|
||||
# Remove saved XenServer scripts directory, but only if it's empty
|
||||
rmdir -p /usr/lib/openvswitch/xs-saved 2>/dev/null
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
/etc/bash_completion.d/ovs-appctl-bashcomp.bash
|
||||
/etc/bash_completion.d/ovs-vsctl-bashcomp.bash
|
||||
/etc/init.d/openvswitch
|
||||
/etc/init.d/openvswitch-xapi-update
|
||||
/etc/xapi.d/plugins/openvswitch-cfg-update
|
||||
/etc/xensource/bugtool/*
|
||||
/etc/logrotate.d/openvswitch
|
||||
/etc/profile.d/openvswitch.sh
|
||||
/usr/share/openvswitch/python/
|
||||
/usr/share/openvswitch/bugtool-plugins/*
|
||||
/usr/share/openvswitch/scripts/ovs-check-dead-ifs
|
||||
/usr/share/openvswitch/scripts/ovs-xapi-sync
|
||||
/usr/share/openvswitch/scripts/interface-reconfigure
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigure.py
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigureBridge.py
|
||||
/usr/share/openvswitch/scripts/InterfaceReconfigureVswitch.py
|
||||
/usr/share/openvswitch/scripts/vif
|
||||
/usr/share/openvswitch/scripts/sysconfig.template
|
||||
/usr/share/openvswitch/scripts/ovs-bugtool-*
|
||||
/usr/share/openvswitch/scripts/ovs-save
|
||||
/usr/share/openvswitch/scripts/ovs-ctl
|
||||
/usr/share/openvswitch/scripts/ovs-lib
|
||||
/usr/share/openvswitch/scripts/ovs-vtep
|
||||
/usr/share/openvswitch/vswitch.ovsschema
|
||||
/usr/share/openvswitch/local-config.ovsschema
|
||||
/usr/share/openvswitch/vtep.ovsschema
|
||||
/usr/sbin/ovs-bugtool
|
||||
/usr/sbin/ovs-vswitchd
|
||||
/usr/sbin/ovsdb-server
|
||||
/usr/bin/ovs-appctl
|
||||
/usr/bin/ovs-dpctl
|
||||
/usr/bin/ovs-dpctl-top
|
||||
/usr/bin/ovs-docker
|
||||
/usr/bin/ovs-ofctl
|
||||
/usr/bin/ovs-parse-backtrace
|
||||
/usr/bin/ovs-pcap
|
||||
/usr/bin/ovs-tcpundump
|
||||
/usr/bin/ovs-vlan-test
|
||||
/usr/bin/ovs-vsctl
|
||||
/usr/bin/ovsdb-client
|
||||
/usr/bin/ovsdb-tool
|
||||
/usr/bin/vtep-ctl
|
||||
/usr/bin/ovs-tcpdump
|
||||
/usr/lib/xsconsole/plugins-base/XSFeatureVSwitch.py
|
||||
/usr/share/man/man1/ovsdb-client.1.gz
|
||||
/usr/share/man/man1/ovsdb-server.1.gz
|
||||
/usr/share/man/man1/ovsdb-tool.1.gz
|
||||
/usr/share/man/man5/ovsdb.local-config.5.gz
|
||||
/usr/share/man/man5/ovsdb-server.5.gz
|
||||
/usr/share/man/man5/ovs-vswitchd.conf.db.5.gz
|
||||
/usr/share/man/man5/vtep.5.gz
|
||||
/usr/share/man/man7/ovs-fields.7.gz
|
||||
/usr/share/man/man8/ovs-appctl.8.gz
|
||||
/usr/share/man/man8/ovs-bugtool.8.gz
|
||||
/usr/share/man/man8/ovs-ctl.8.gz
|
||||
/usr/share/man/man8/ovs-dpctl.8.gz
|
||||
/usr/share/man/man8/ovs-dpctl-top.8.gz
|
||||
/usr/share/man/man8/ovs-ofctl.8.gz
|
||||
/usr/share/man/man8/ovs-parse-backtrace.8.gz
|
||||
/usr/share/man/man1/ovs-pcap.1.gz
|
||||
/usr/share/man/man1/ovs-tcpundump.1.gz
|
||||
/usr/share/man/man8/ovs-vlan-test.8.gz
|
||||
/usr/share/man/man8/ovs-vsctl.8.gz
|
||||
/usr/share/man/man8/ovs-vswitchd.8.gz
|
||||
/usr/share/man/man8/vtep-ctl.8.gz
|
||||
/usr/share/man/man8/ovs-tcpdump.8.gz
|
||||
/var/lib/openvswitch
|
||||
/var/log/openvswitch
|
||||
%exclude /usr/lib/xsconsole/plugins-base/*.py[co]
|
||||
%exclude /usr/share/openvswitch/scripts/*.py[co]
|
||||
%exclude /usr/share/openvswitch/python/*.py[co]
|
||||
%exclude /usr/share/openvswitch/python/ovs/*.py[co]
|
||||
%exclude /usr/share/openvswitch/python/ovs/db/*.py[co]
|
||||
|
||||
%files %{module_package}
|
||||
/lib/modules/%{xen_version}/extra/openvswitch/openvswitch.ko
|
||||
/lib/modules/%{xen_version}/extra/openvswitch/vport-*.ko
|
@@ -1,972 +0,0 @@
|
||||
# Copyright (c) 2008,2009 Citrix Systems, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation; version 2.1 only. with the special
|
||||
# exception on linking described in file LICENSE.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
import sys
|
||||
import syslog
|
||||
import os
|
||||
|
||||
from xml.dom.minidom import getDOMImplementation
|
||||
from xml.dom.minidom import parse as parseXML
|
||||
|
||||
the_root_prefix = ""
|
||||
def root_prefix():
|
||||
"""Returns a string to prefix to all file name references, which
|
||||
is useful for testing."""
|
||||
return the_root_prefix
|
||||
def set_root_prefix(prefix):
|
||||
global the_root_prefix
|
||||
the_root_prefix = prefix
|
||||
|
||||
log_destination = "syslog"
|
||||
def get_log_destination():
|
||||
"""Returns the current log destination.
|
||||
'syslog' means "log to syslog".
|
||||
'stderr' means "log to stderr"."""
|
||||
return log_destination
|
||||
def set_log_destination(dest):
|
||||
global log_destination
|
||||
log_destination = dest
|
||||
|
||||
#
|
||||
# Logging.
|
||||
#
|
||||
|
||||
def log(s):
|
||||
if get_log_destination() == 'syslog':
|
||||
syslog.syslog(s)
|
||||
else:
|
||||
sys.stderr.write(s + '\n')
|
||||
sys.stderr.flush()
|
||||
|
||||
#
|
||||
# Exceptions.
|
||||
#
|
||||
|
||||
class Error(Exception):
|
||||
def __init__(self, msg):
|
||||
Exception.__init__(self)
|
||||
self.msg = msg
|
||||
|
||||
#
|
||||
# Run external utilities
|
||||
#
|
||||
|
||||
def run_command(command):
|
||||
log("Running command: " + ' '.join(command))
|
||||
rc = os.spawnl(os.P_WAIT, root_prefix() + command[0], *command)
|
||||
if rc != 0:
|
||||
log("Command failed %d: " % rc + ' '.join(command))
|
||||
return False
|
||||
return True
|
||||
|
||||
#
|
||||
# Configuration File Handling.
|
||||
#
|
||||
|
||||
class ConfigurationFile(object):
|
||||
"""Write a file, tracking old and new versions.
|
||||
|
||||
Supports writing a new version of a file and applying and
|
||||
reverting those changes.
|
||||
"""
|
||||
|
||||
__STATE = {"OPEN":"OPEN",
|
||||
"NOT-APPLIED":"NOT-APPLIED", "APPLIED":"APPLIED",
|
||||
"REVERTED":"REVERTED", "COMMITTED": "COMMITTED"}
|
||||
|
||||
def __init__(self, path):
|
||||
dirname,basename = os.path.split(path)
|
||||
|
||||
self.__state = self.__STATE['OPEN']
|
||||
self.__children = []
|
||||
|
||||
self.__path = os.path.join(dirname, basename)
|
||||
self.__oldpath = os.path.join(dirname, "." + basename + ".xapi-old")
|
||||
self.__newpath = os.path.join(dirname, "." + basename + ".xapi-new")
|
||||
|
||||
self.__f = open(self.__newpath, "w")
|
||||
|
||||
def attach_child(self, child):
|
||||
self.__children.append(child)
|
||||
|
||||
def path(self):
|
||||
return self.__path
|
||||
|
||||
def readlines(self):
|
||||
try:
|
||||
return open(self.path()).readlines()
|
||||
except:
|
||||
return ""
|
||||
|
||||
def write(self, args):
|
||||
if self.__state != self.__STATE['OPEN']:
|
||||
raise Error("Attempt to write to file in state %s" % self.__state)
|
||||
self.__f.write(args)
|
||||
|
||||
def close(self):
|
||||
if self.__state != self.__STATE['OPEN']:
|
||||
raise Error("Attempt to close file in state %s" % self.__state)
|
||||
|
||||
self.__f.close()
|
||||
self.__state = self.__STATE['NOT-APPLIED']
|
||||
|
||||
def changed(self):
|
||||
if self.__state != self.__STATE['NOT-APPLIED']:
|
||||
raise Error("Attempt to compare file in state %s" % self.__state)
|
||||
|
||||
return True
|
||||
|
||||
def apply(self):
|
||||
if self.__state != self.__STATE['NOT-APPLIED']:
|
||||
raise Error("Attempt to apply configuration from state %s" % self.__state)
|
||||
|
||||
for child in self.__children:
|
||||
child.apply()
|
||||
|
||||
log("Applying changes to %s configuration" % self.__path)
|
||||
|
||||
# Remove previous backup.
|
||||
if os.access(self.__oldpath, os.F_OK):
|
||||
os.unlink(self.__oldpath)
|
||||
|
||||
# Save current configuration.
|
||||
if os.access(self.__path, os.F_OK):
|
||||
os.link(self.__path, self.__oldpath)
|
||||
os.unlink(self.__path)
|
||||
|
||||
# Apply new configuration.
|
||||
assert(os.path.exists(self.__newpath))
|
||||
os.link(self.__newpath, self.__path)
|
||||
|
||||
# Remove temporary file.
|
||||
os.unlink(self.__newpath)
|
||||
|
||||
self.__state = self.__STATE['APPLIED']
|
||||
|
||||
def revert(self):
|
||||
if self.__state != self.__STATE['APPLIED']:
|
||||
raise Error("Attempt to revert configuration from state %s" % self.__state)
|
||||
|
||||
for child in self.__children:
|
||||
child.revert()
|
||||
|
||||
log("Reverting changes to %s configuration" % self.__path)
|
||||
|
||||
# Remove existing new configuration
|
||||
if os.access(self.__newpath, os.F_OK):
|
||||
os.unlink(self.__newpath)
|
||||
|
||||
# Revert new configuration.
|
||||
if os.access(self.__path, os.F_OK):
|
||||
os.link(self.__path, self.__newpath)
|
||||
os.unlink(self.__path)
|
||||
|
||||
# Revert to old configuration.
|
||||
if os.access(self.__oldpath, os.F_OK):
|
||||
os.link(self.__oldpath, self.__path)
|
||||
os.unlink(self.__oldpath)
|
||||
|
||||
# Leave .*.xapi-new as an aid to debugging.
|
||||
|
||||
self.__state = self.__STATE['REVERTED']
|
||||
|
||||
def commit(self):
|
||||
if self.__state != self.__STATE['APPLIED']:
|
||||
raise Error("Attempt to commit configuration from state %s" % self.__state)
|
||||
|
||||
for child in self.__children:
|
||||
child.commit()
|
||||
|
||||
log("Committing changes to %s configuration" % self.__path)
|
||||
|
||||
if os.access(self.__oldpath, os.F_OK):
|
||||
os.unlink(self.__oldpath)
|
||||
if os.access(self.__newpath, os.F_OK):
|
||||
os.unlink(self.__newpath)
|
||||
|
||||
self.__state = self.__STATE['COMMITTED']
|
||||
|
||||
#
|
||||
# Helper functions for encoding/decoding database attributes to/from XML.
|
||||
#
|
||||
|
||||
def _str_to_xml(xml, parent, tag, val):
|
||||
e = xml.createElement(tag)
|
||||
parent.appendChild(e)
|
||||
v = xml.createTextNode(val)
|
||||
e.appendChild(v)
|
||||
def _str_from_xml(n):
|
||||
def getText(nodelist):
|
||||
rc = ""
|
||||
for node in nodelist:
|
||||
if node.nodeType == node.TEXT_NODE:
|
||||
rc = rc + node.data
|
||||
return rc
|
||||
return getText(n.childNodes).strip()
|
||||
|
||||
def _bool_to_xml(xml, parent, tag, val):
|
||||
if val:
|
||||
_str_to_xml(xml, parent, tag, "True")
|
||||
else:
|
||||
_str_to_xml(xml, parent, tag, "False")
|
||||
def _bool_from_xml(n):
|
||||
s = _str_from_xml(n)
|
||||
if s == "True":
|
||||
return True
|
||||
elif s == "False":
|
||||
return False
|
||||
else:
|
||||
raise Error("Unknown boolean value %s" % s)
|
||||
|
||||
def _strlist_to_xml(xml, parent, ltag, itag, val):
|
||||
e = xml.createElement(ltag)
|
||||
parent.appendChild(e)
|
||||
for v in val:
|
||||
c = xml.createElement(itag)
|
||||
e.appendChild(c)
|
||||
cv = xml.createTextNode(v)
|
||||
c.appendChild(cv)
|
||||
def _strlist_from_xml(n, ltag, itag):
|
||||
ret = []
|
||||
for n in n.childNodes:
|
||||
if n.nodeName == itag:
|
||||
ret.append(_str_from_xml(n))
|
||||
return ret
|
||||
|
||||
def _map_to_xml(xml, parent, tag, val, attrs):
|
||||
e = xml.createElement(tag)
|
||||
parent.appendChild(e)
|
||||
for n,v in val.items():
|
||||
if n in attrs:
|
||||
_str_to_xml(xml, e, n, v)
|
||||
else:
|
||||
log("Unknown other-config attribute: %s" % n)
|
||||
|
||||
def _map_from_xml(n, attrs):
|
||||
ret = {}
|
||||
for n in n.childNodes:
|
||||
if n.nodeName in attrs:
|
||||
ret[n.nodeName] = _str_from_xml(n)
|
||||
return ret
|
||||
|
||||
def _otherconfig_to_xml(xml, parent, val, attrs):
|
||||
return _map_to_xml(xml, parent, "other_config", val, attrs)
|
||||
def _otherconfig_from_xml(n, attrs):
|
||||
return _map_from_xml(n, attrs)
|
||||
|
||||
#
|
||||
# Definitions of the database objects (and their attributes) used by interface-reconfigure.
|
||||
#
|
||||
# Each object is defined by a dictionary mapping an attribute name in
|
||||
# the xapi database to a tuple containing two items:
|
||||
# - a function which takes this attribute and encodes it as XML.
|
||||
# - a function which takes XML and decocdes it into a value.
|
||||
#
|
||||
# other-config attributes are specified as a simple array of strings
|
||||
|
||||
_PIF_XML_TAG = "pif"
|
||||
_VLAN_XML_TAG = "vlan"
|
||||
_TUNNEL_XML_TAG = "tunnel"
|
||||
_BOND_XML_TAG = "bond"
|
||||
_NETWORK_XML_TAG = "network"
|
||||
_POOL_XML_TAG = "pool"
|
||||
|
||||
_ETHTOOL_OTHERCONFIG_ATTRS = ['ethtool-%s' % x for x in ['autoneg', 'speed', 'duplex', 'rx', 'tx', 'sg', 'tso', 'ufo', 'gso', 'gro', 'lro'] ]
|
||||
|
||||
_PIF_OTHERCONFIG_ATTRS = [ 'domain', 'peerdns', 'defaultroute', 'mtu', 'static-routes' ] + \
|
||||
[ 'bond-%s' % x for x in ['mode', 'miimon', 'downdelay', 'updelay', 'use_carrier', 'hashing-algorithm'] ] + \
|
||||
[ 'vlan-bug-workaround' ] + \
|
||||
_ETHTOOL_OTHERCONFIG_ATTRS
|
||||
|
||||
_PIF_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
|
||||
'management': (_bool_to_xml,_bool_from_xml),
|
||||
'network': (_str_to_xml,_str_from_xml),
|
||||
'device': (_str_to_xml,_str_from_xml),
|
||||
'bond_master_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'bond_master_of', 'slave', v),
|
||||
lambda n: _strlist_from_xml(n, 'bond_master_of', 'slave')),
|
||||
'bond_slave_of': (_str_to_xml,_str_from_xml),
|
||||
'VLAN': (_str_to_xml,_str_from_xml),
|
||||
'VLAN_master_of': (_str_to_xml,_str_from_xml),
|
||||
'VLAN_slave_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'VLAN_slave_of', 'master', v),
|
||||
lambda n: _strlist_from_xml(n, 'VLAN_slave_Of', 'master')),
|
||||
'tunnel_access_PIF_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'tunnel_access_PIF_of', 'pif', v),
|
||||
lambda n: _strlist_from_xml(n, 'tunnel_access_PIF_of', 'pif')),
|
||||
'tunnel_transport_PIF_of': (lambda x, p, t, v: _strlist_to_xml(x, p, 'tunnel_transport_PIF_of', 'pif', v),
|
||||
lambda n: _strlist_from_xml(n, 'tunnel_transport_PIF_of', 'pif')),
|
||||
'ip_configuration_mode': (_str_to_xml,_str_from_xml),
|
||||
'IP': (_str_to_xml,_str_from_xml),
|
||||
'netmask': (_str_to_xml,_str_from_xml),
|
||||
'gateway': (_str_to_xml,_str_from_xml),
|
||||
'DNS': (_str_to_xml,_str_from_xml),
|
||||
'MAC': (_str_to_xml,_str_from_xml),
|
||||
'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _PIF_OTHERCONFIG_ATTRS),
|
||||
lambda n: _otherconfig_from_xml(n, _PIF_OTHERCONFIG_ATTRS)),
|
||||
|
||||
# Special case: We write the current value
|
||||
# PIF.currently-attached to the cache but since it will
|
||||
# not be valid when we come to use the cache later
|
||||
# (i.e. after a reboot) we always read it as False.
|
||||
'currently_attached': (_bool_to_xml, lambda n: False),
|
||||
}
|
||||
|
||||
_VLAN_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
|
||||
'tagged_PIF': (_str_to_xml,_str_from_xml),
|
||||
'untagged_PIF': (_str_to_xml,_str_from_xml),
|
||||
}
|
||||
|
||||
_TUNNEL_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
|
||||
'access_PIF': (_str_to_xml,_str_from_xml),
|
||||
'transport_PIF': (_str_to_xml,_str_from_xml),
|
||||
}
|
||||
_BOND_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
|
||||
'master': (_str_to_xml,_str_from_xml),
|
||||
'slaves': (lambda x, p, t, v: _strlist_to_xml(x, p, 'slaves', 'slave', v),
|
||||
lambda n: _strlist_from_xml(n, 'slaves', 'slave')),
|
||||
}
|
||||
|
||||
_NETWORK_OTHERCONFIG_ATTRS = [ 'mtu',
|
||||
'static-routes',
|
||||
'vswitch-controller-fail-mode',
|
||||
'vswitch-disable-in-band' ] \
|
||||
+ _ETHTOOL_OTHERCONFIG_ATTRS
|
||||
|
||||
_NETWORK_ATTRS = { 'uuid': (_str_to_xml,_str_from_xml),
|
||||
'bridge': (_str_to_xml,_str_from_xml),
|
||||
'MTU': (_str_to_xml,_str_from_xml),
|
||||
'PIFs': (lambda x, p, t, v: _strlist_to_xml(x, p, 'PIFs', 'PIF', v),
|
||||
lambda n: _strlist_from_xml(n, 'PIFs', 'PIF')),
|
||||
'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _NETWORK_OTHERCONFIG_ATTRS),
|
||||
lambda n: _otherconfig_from_xml(n, _NETWORK_OTHERCONFIG_ATTRS)),
|
||||
}
|
||||
|
||||
_POOL_OTHERCONFIG_ATTRS = ['vswitch-controller-fail-mode']
|
||||
|
||||
_POOL_ATTRS = { 'other_config': (lambda x, p, t, v: _otherconfig_to_xml(x, p, v, _POOL_OTHERCONFIG_ATTRS),
|
||||
lambda n: _otherconfig_from_xml(n, _POOL_OTHERCONFIG_ATTRS)),
|
||||
}
|
||||
|
||||
#
|
||||
# Database Cache object
|
||||
#
|
||||
|
||||
_db = None
|
||||
|
||||
def db():
|
||||
assert(_db is not None)
|
||||
return _db
|
||||
|
||||
def db_init_from_cache(cache):
|
||||
global _db
|
||||
assert(_db is None)
|
||||
_db = DatabaseCache(cache_file=cache)
|
||||
|
||||
def db_init_from_xenapi(session):
|
||||
global _db
|
||||
assert(_db is None)
|
||||
_db = DatabaseCache(session_ref=session)
|
||||
|
||||
class DatabaseCache(object):
|
||||
def __read_xensource_inventory(self):
|
||||
filename = root_prefix() + "/etc/xensource-inventory"
|
||||
f = open(filename, "r")
|
||||
lines = [x.strip("\n") for x in f.readlines()]
|
||||
f.close()
|
||||
|
||||
defs = [ (l[:l.find("=")], l[(l.find("=") + 1):]) for l in lines ]
|
||||
defs = [ (a, b.strip("'")) for (a,b) in defs ]
|
||||
|
||||
return dict(defs)
|
||||
|
||||
def __pif_on_host(self,pif):
|
||||
return pif in self.__pifs
|
||||
|
||||
def __get_pif_records_from_xapi(self, session, host):
|
||||
self.__pifs = {}
|
||||
for (p,rec) in session.xenapi.PIF.get_all_records().items():
|
||||
if rec['host'] != host:
|
||||
continue
|
||||
self.__pifs[p] = {}
|
||||
for f in _PIF_ATTRS:
|
||||
self.__pifs[p][f] = rec[f]
|
||||
self.__pifs[p]['other_config'] = {}
|
||||
for f in _PIF_OTHERCONFIG_ATTRS:
|
||||
if f not in rec['other_config']: continue
|
||||
self.__pifs[p]['other_config'][f] = rec['other_config'][f]
|
||||
|
||||
def __get_vlan_records_from_xapi(self, session):
|
||||
self.__vlans = {}
|
||||
for (v,rec) in session.xenapi.VLAN.get_all_records().items():
|
||||
if not self.__pif_on_host(rec['untagged_PIF']):
|
||||
continue
|
||||
self.__vlans[v] = {}
|
||||
for f in _VLAN_ATTRS:
|
||||
self.__vlans[v][f] = rec[f]
|
||||
|
||||
def __get_tunnel_records_from_xapi(self, session):
|
||||
self.__tunnels = {}
|
||||
for t in session.xenapi.tunnel.get_all():
|
||||
rec = session.xenapi.tunnel.get_record(t)
|
||||
if not self.__pif_on_host(rec['transport_PIF']):
|
||||
continue
|
||||
self.__tunnels[t] = {}
|
||||
for f in _TUNNEL_ATTRS:
|
||||
self.__tunnels[t][f] = rec[f]
|
||||
|
||||
def __get_bond_records_from_xapi(self, session):
|
||||
self.__bonds = {}
|
||||
for (b,rec) in session.xenapi.Bond.get_all_records().items():
|
||||
if not self.__pif_on_host(rec['master']):
|
||||
continue
|
||||
self.__bonds[b] = {}
|
||||
for f in _BOND_ATTRS:
|
||||
self.__bonds[b][f] = rec[f]
|
||||
|
||||
def __get_network_records_from_xapi(self, session):
|
||||
self.__networks = {}
|
||||
for (n,rec) in session.xenapi.network.get_all_records().items():
|
||||
self.__networks[n] = {}
|
||||
for f in _NETWORK_ATTRS:
|
||||
if f == "PIFs":
|
||||
# drop PIFs on other hosts
|
||||
self.__networks[n][f] = [p for p in rec[f] if self.__pif_on_host(p)]
|
||||
elif f == "MTU" and f not in rec:
|
||||
# XenServer 5.5 network records did not have an
|
||||
# MTU field, so allow this to be missing.
|
||||
pass
|
||||
else:
|
||||
self.__networks[n][f] = rec[f]
|
||||
self.__networks[n]['other_config'] = {}
|
||||
for f in _NETWORK_OTHERCONFIG_ATTRS:
|
||||
if f not in rec['other_config']: continue
|
||||
self.__networks[n]['other_config'][f] = rec['other_config'][f]
|
||||
|
||||
def __get_pool_records_from_xapi(self, session):
|
||||
self.__pools = {}
|
||||
for p in session.xenapi.pool.get_all():
|
||||
rec = session.xenapi.pool.get_record(p)
|
||||
|
||||
self.__pools[p] = {}
|
||||
|
||||
for f in _POOL_ATTRS:
|
||||
self.__pools[p][f] = rec[f]
|
||||
|
||||
for f in _POOL_OTHERCONFIG_ATTRS:
|
||||
if f in rec['other_config']:
|
||||
self.__pools[p]['other_config'][f] = rec['other_config'][f]
|
||||
|
||||
def __to_xml(self, xml, parent, key, ref, rec, attrs):
|
||||
"""Encode a database object as XML"""
|
||||
e = xml.createElement(key)
|
||||
parent.appendChild(e)
|
||||
if ref:
|
||||
e.setAttribute('ref', ref)
|
||||
|
||||
for n,v in rec.items():
|
||||
if n in attrs:
|
||||
h,_ = attrs[n]
|
||||
h(xml, e, n, v)
|
||||
else:
|
||||
raise Error("Unknown attribute %s" % n)
|
||||
def __from_xml(self, e, attrs):
|
||||
"""Decode a database object from XML"""
|
||||
ref = e.attributes['ref'].value
|
||||
rec = {}
|
||||
for n in e.childNodes:
|
||||
if n.nodeName in attrs:
|
||||
_,h = attrs[n.nodeName]
|
||||
rec[n.nodeName] = h(n)
|
||||
return (ref,rec)
|
||||
|
||||
def __init__(self, session_ref=None, cache_file=None):
|
||||
if session_ref and cache_file:
|
||||
raise Error("can't specify session reference and cache file")
|
||||
if cache_file == None:
|
||||
import XenAPI
|
||||
session = XenAPI.xapi_local()
|
||||
|
||||
if not session_ref:
|
||||
log("No session ref given on command line, logging in.")
|
||||
session.xenapi.login_with_password("root", "")
|
||||
else:
|
||||
session._session = session_ref
|
||||
|
||||
try:
|
||||
|
||||
inventory = self.__read_xensource_inventory()
|
||||
assert('INSTALLATION_UUID' in inventory)
|
||||
log("host uuid is %s" % inventory['INSTALLATION_UUID'])
|
||||
|
||||
host = session.xenapi.host.get_by_uuid(inventory['INSTALLATION_UUID'])
|
||||
|
||||
self.__get_pif_records_from_xapi(session, host)
|
||||
self.__get_pool_records_from_xapi(session)
|
||||
self.__get_tunnel_records_from_xapi(session)
|
||||
self.__get_vlan_records_from_xapi(session)
|
||||
self.__get_bond_records_from_xapi(session)
|
||||
self.__get_network_records_from_xapi(session)
|
||||
finally:
|
||||
if not session_ref:
|
||||
session.xenapi.session.logout()
|
||||
else:
|
||||
log("Loading xapi database cache from %s" % cache_file)
|
||||
|
||||
xml = parseXML(root_prefix() + cache_file)
|
||||
|
||||
self.__pifs = {}
|
||||
self.__bonds = {}
|
||||
self.__vlans = {}
|
||||
self.__pools = {}
|
||||
self.__tunnels = {}
|
||||
self.__networks = {}
|
||||
|
||||
assert(len(xml.childNodes) == 1)
|
||||
toplevel = xml.childNodes[0]
|
||||
|
||||
assert(toplevel.nodeName == "xenserver-network-configuration")
|
||||
|
||||
for n in toplevel.childNodes:
|
||||
if n.nodeName == "#text":
|
||||
pass
|
||||
elif n.nodeName == _PIF_XML_TAG:
|
||||
(ref,rec) = self.__from_xml(n, _PIF_ATTRS)
|
||||
self.__pifs[ref] = rec
|
||||
elif n.nodeName == _BOND_XML_TAG:
|
||||
(ref,rec) = self.__from_xml(n, _BOND_ATTRS)
|
||||
self.__bonds[ref] = rec
|
||||
elif n.nodeName == _VLAN_XML_TAG:
|
||||
(ref,rec) = self.__from_xml(n, _VLAN_ATTRS)
|
||||
self.__vlans[ref] = rec
|
||||
elif n.nodeName == _TUNNEL_XML_TAG:
|
||||
(ref,rec) = self.__from_xml(n, _TUNNEL_ATTRS)
|
||||
self.__vlans[ref] = rec
|
||||
elif n.nodeName == _NETWORK_XML_TAG:
|
||||
(ref,rec) = self.__from_xml(n, _NETWORK_ATTRS)
|
||||
self.__networks[ref] = rec
|
||||
elif n.nodeName == _POOL_XML_TAG:
|
||||
(ref,rec) = self.__from_xml(n, _POOL_ATTRS)
|
||||
self.__pools[ref] = rec
|
||||
else:
|
||||
raise Error("Unknown XML element %s" % n.nodeName)
|
||||
|
||||
def save(self, cache_file):
|
||||
|
||||
xml = getDOMImplementation().createDocument(
|
||||
None, "xenserver-network-configuration", None)
|
||||
for (ref,rec) in self.__pifs.items():
|
||||
self.__to_xml(xml, xml.documentElement, _PIF_XML_TAG, ref, rec, _PIF_ATTRS)
|
||||
for (ref,rec) in self.__bonds.items():
|
||||
self.__to_xml(xml, xml.documentElement, _BOND_XML_TAG, ref, rec, _BOND_ATTRS)
|
||||
for (ref,rec) in self.__vlans.items():
|
||||
self.__to_xml(xml, xml.documentElement, _VLAN_XML_TAG, ref, rec, _VLAN_ATTRS)
|
||||
for (ref,rec) in self.__tunnels.items():
|
||||
self.__to_xml(xml, xml.documentElement, _TUNNEL_XML_TAG, ref, rec, _TUNNEL_ATTRS)
|
||||
for (ref,rec) in self.__networks.items():
|
||||
self.__to_xml(xml, xml.documentElement, _NETWORK_XML_TAG, ref, rec,
|
||||
_NETWORK_ATTRS)
|
||||
for (ref,rec) in self.__pools.items():
|
||||
self.__to_xml(xml, xml.documentElement, _POOL_XML_TAG, ref, rec, _POOL_ATTRS)
|
||||
|
||||
temp_file = cache_file + ".%d" % os.getpid()
|
||||
f = open(temp_file, 'w')
|
||||
f.write(xml.toprettyxml())
|
||||
f.close()
|
||||
os.rename(temp_file, cache_file)
|
||||
|
||||
def get_pif_by_uuid(self, uuid):
|
||||
pifs = map(lambda ref_rec: ref_rec[0],
|
||||
filter(lambda ref_rec: uuid == ref_rec[1]['uuid'],
|
||||
self.__pifs.items()))
|
||||
if len(pifs) == 0:
|
||||
raise Error("Unknown PIF \"%s\"" % uuid)
|
||||
elif len(pifs) > 1:
|
||||
raise Error("Non-unique PIF \"%s\"" % uuid)
|
||||
|
||||
return pifs[0]
|
||||
|
||||
def get_pifs_by_device(self, device):
|
||||
return list(map(lambda ref_rec: ref_rec[0],
|
||||
list(filter(lambda ref_rec: ref_rec[1]['device'] == device,
|
||||
self.__pifs.items()))))
|
||||
|
||||
def get_networks_with_bridge(self, bridge):
|
||||
return list(map(lambda ref_rec: ref_rec[0],
|
||||
list(filter(lambda ref_rec: ref_rec[1]['bridge'] == bridge,
|
||||
self.__networks.items()))))
|
||||
|
||||
def get_network_by_bridge(self, bridge):
|
||||
#Assumes one network has bridge.
|
||||
try:
|
||||
return self.get_networks_with_bridge(bridge)[0]
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def get_pif_by_bridge(self, bridge):
|
||||
networks = self.get_networks_with_bridge(bridge)
|
||||
|
||||
if len(networks) == 0:
|
||||
raise Error("No matching network \"%s\"" % bridge)
|
||||
|
||||
answer = None
|
||||
for network in networks:
|
||||
nwrec = self.get_network_record(network)
|
||||
for pif in nwrec['PIFs']:
|
||||
pifrec = self.get_pif_record(pif)
|
||||
if answer:
|
||||
raise Error("Multiple PIFs on host for network %s" % (bridge))
|
||||
answer = pif
|
||||
if not answer:
|
||||
raise Error("No PIF on host for network %s" % (bridge))
|
||||
return answer
|
||||
|
||||
def get_pif_record(self, pif):
|
||||
if pif in self.__pifs:
|
||||
return self.__pifs[pif]
|
||||
raise Error("Unknown PIF \"%s\"" % pif)
|
||||
def get_all_pifs(self):
|
||||
return self.__pifs
|
||||
def pif_exists(self, pif):
|
||||
return pif in self.__pifs
|
||||
|
||||
def get_management_pif(self):
|
||||
""" Returns the management pif on host
|
||||
"""
|
||||
all = self.get_all_pifs()
|
||||
for pif in all:
|
||||
pifrec = self.get_pif_record(pif)
|
||||
if pifrec['management']: return pif
|
||||
return None
|
||||
|
||||
def get_network_record(self, network):
|
||||
if network in self.__networks:
|
||||
return self.__networks[network]
|
||||
raise Error("Unknown network \"%s\"" % network)
|
||||
|
||||
def get_bond_record(self, bond):
|
||||
if bond in self.__bonds:
|
||||
return self.__bonds[bond]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_vlan_record(self, vlan):
|
||||
if vlan in self.__vlans:
|
||||
return self.__vlans[vlan]
|
||||
else:
|
||||
return None
|
||||
|
||||
def get_pool_record(self):
|
||||
if len(self.__pools) > 0:
|
||||
return list(self.__pools.values())[0]
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
PIF_OTHERCONFIG_DEFAULTS = {'gro': 'off', 'lro': 'off'}
|
||||
|
||||
def ethtool_settings(oc, defaults = {}):
|
||||
settings = []
|
||||
if 'ethtool-speed' in oc:
|
||||
val = oc['ethtool-speed']
|
||||
if val in ["10", "100", "1000"]:
|
||||
settings += ['speed', val]
|
||||
else:
|
||||
log("Invalid value for ethtool-speed = %s. Must be 10|100|1000." % val)
|
||||
if 'ethtool-duplex' in oc:
|
||||
val = oc['ethtool-duplex']
|
||||
if val in ["half", "full"]:
|
||||
settings += ['duplex', val]
|
||||
else:
|
||||
log("Invalid value for ethtool-duplex = %s. Must be half|full." % val)
|
||||
if 'ethtool-autoneg' in oc:
|
||||
val = oc['ethtool-autoneg']
|
||||
if val in ["true", "on"]:
|
||||
settings += ['autoneg', 'on']
|
||||
elif val in ["false", "off"]:
|
||||
settings += ['autoneg', 'off']
|
||||
else:
|
||||
log("Invalid value for ethtool-autoneg = %s. Must be on|true|off|false." % val)
|
||||
offload = []
|
||||
for opt in ("rx", "tx", "sg", "tso", "ufo", "gso", "gro", "lro"):
|
||||
if "ethtool-" + opt in oc:
|
||||
val = oc["ethtool-" + opt]
|
||||
if val in ["true", "on"]:
|
||||
offload += [opt, 'on']
|
||||
elif val in ["false", "off"]:
|
||||
offload += [opt, 'off']
|
||||
else:
|
||||
log("Invalid value for ethtool-%s = %s. Must be on|true|off|false." % (opt, val))
|
||||
elif opt in defaults:
|
||||
offload += [opt, defaults[opt]]
|
||||
return settings,offload
|
||||
|
||||
# By default the MTU is taken from the Network.MTU setting for VIF,
|
||||
# PIF and Bridge. However it is possible to override this by using
|
||||
# {VIF,PIF,Network}.other-config:mtu.
|
||||
#
|
||||
# type parameter is a string describing the object that the oc parameter
|
||||
# is from. e.g. "PIF", "Network"
|
||||
def mtu_setting(nw, type, oc):
|
||||
mtu = None
|
||||
|
||||
nwrec = db().get_network_record(nw)
|
||||
if 'MTU' in nwrec:
|
||||
mtu = nwrec['MTU']
|
||||
else:
|
||||
mtu = "1500"
|
||||
|
||||
if 'mtu' in oc:
|
||||
log("Override Network.MTU setting on bridge %s from %s.MTU is %s" % \
|
||||
(nwrec['bridge'], type, mtu))
|
||||
mtu = oc['mtu']
|
||||
|
||||
if mtu is not None:
|
||||
try:
|
||||
int(mtu) # Check that the value is an integer
|
||||
return mtu
|
||||
except ValueError as x:
|
||||
log("Invalid value for mtu = %s" % mtu)
|
||||
|
||||
return None
|
||||
|
||||
#
|
||||
# IP Network Devices -- network devices with IP configuration
|
||||
#
|
||||
def pif_ipdev_name(pif):
|
||||
"""Return the ipdev name associated with pif"""
|
||||
pifrec = db().get_pif_record(pif)
|
||||
nwrec = db().get_network_record(pifrec['network'])
|
||||
|
||||
if nwrec['bridge']:
|
||||
# TODO: sanity check that nwrec['bridgeless'] != 'true'
|
||||
return nwrec['bridge']
|
||||
else:
|
||||
# TODO: sanity check that nwrec['bridgeless'] == 'true'
|
||||
return pif_netdev_name(pif)
|
||||
|
||||
#
|
||||
# Bare Network Devices -- network devices without IP configuration
|
||||
#
|
||||
|
||||
def netdev_exists(netdev):
|
||||
return os.path.exists(root_prefix() + "/sys/class/net/" + netdev)
|
||||
|
||||
|
||||
def unicode_2to3(string):
|
||||
if sys.version_info < (3,):
|
||||
return string.encode()
|
||||
return string
|
||||
|
||||
|
||||
def pif_netdev_name(pif):
|
||||
"""Get the netdev name for a PIF."""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
if pif_is_vlan(pif):
|
||||
return unicode_2to3("%(device)s.%(VLAN)s" % pifrec)
|
||||
else:
|
||||
return unicode_2to3(pifrec['device'])
|
||||
|
||||
#
|
||||
# Bridges
|
||||
#
|
||||
|
||||
def pif_is_bridged(pif):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
nwrec = db().get_network_record(pifrec['network'])
|
||||
|
||||
if nwrec['bridge']:
|
||||
# TODO: sanity check that nwrec['bridgeless'] != 'true'
|
||||
return True
|
||||
else:
|
||||
# TODO: sanity check that nwrec['bridgeless'] == 'true'
|
||||
return False
|
||||
|
||||
def pif_bridge_name(pif):
|
||||
"""Return the bridge name of a pif.
|
||||
|
||||
PIF must be a bridged PIF."""
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
nwrec = db().get_network_record(pifrec['network'])
|
||||
|
||||
if nwrec['bridge']:
|
||||
return nwrec['bridge']
|
||||
else:
|
||||
raise Error("PIF %(uuid)s does not have a bridge name" % pifrec)
|
||||
|
||||
#
|
||||
# Bonded PIFs
|
||||
#
|
||||
def pif_is_bond(pif):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
return len(pifrec['bond_master_of']) > 0
|
||||
|
||||
def pif_get_bond_masters(pif):
|
||||
"""Returns a list of PIFs which are bond masters of this PIF"""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
bso = pifrec['bond_slave_of']
|
||||
|
||||
# bond-slave-of is currently a single reference but in principle a
|
||||
# PIF could be a member of several bonds which are not
|
||||
# concurrently attached. Be robust to this possibility.
|
||||
if not bso or bso == "OpaqueRef:NULL":
|
||||
bso = []
|
||||
elif not type(bso) == list:
|
||||
bso = [bso]
|
||||
|
||||
bondrecs = [db().get_bond_record(bond) for bond in bso]
|
||||
bondrecs = [rec for rec in bondrecs if rec]
|
||||
|
||||
return [bond['master'] for bond in bondrecs]
|
||||
|
||||
def pif_get_bond_slaves(pif):
|
||||
"""Returns a list of PIFs which make up the given bonded pif."""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
bmo = pifrec['bond_master_of']
|
||||
if len(bmo) > 1:
|
||||
raise Error("Bond-master-of contains too many elements")
|
||||
|
||||
if len(bmo) == 0:
|
||||
return []
|
||||
|
||||
bondrec = db().get_bond_record(bmo[0])
|
||||
if not bondrec:
|
||||
raise Error("No bond record for bond master PIF")
|
||||
|
||||
return bondrec['slaves']
|
||||
|
||||
#
|
||||
# VLAN PIFs
|
||||
#
|
||||
|
||||
def pif_is_vlan(pif):
|
||||
return db().get_pif_record(pif)['VLAN'] != '-1'
|
||||
|
||||
def pif_get_vlan_slave(pif):
|
||||
"""Find the PIF which is the VLAN slave of pif.
|
||||
|
||||
Returns the 'physical' PIF underneath the a VLAN PIF @pif."""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
vlan = pifrec['VLAN_master_of']
|
||||
if not vlan or vlan == "OpaqueRef:NULL":
|
||||
raise Error("PIF is not a VLAN master")
|
||||
|
||||
vlanrec = db().get_vlan_record(vlan)
|
||||
if not vlanrec:
|
||||
raise Error("No VLAN record found for PIF")
|
||||
|
||||
return vlanrec['tagged_PIF']
|
||||
|
||||
def pif_get_vlan_masters(pif):
|
||||
"""Returns a list of PIFs which are VLANs on top of the given pif."""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
vlans = [db().get_vlan_record(v) for v in pifrec['VLAN_slave_of']]
|
||||
return [v['untagged_PIF'] for v in vlans if v and db().pif_exists(v['untagged_PIF'])]
|
||||
|
||||
#
|
||||
# Tunnel PIFs
|
||||
#
|
||||
def pif_is_tunnel(pif):
|
||||
return len(db().get_pif_record(pif)['tunnel_access_PIF_of']) > 0
|
||||
|
||||
#
|
||||
# Datapath base class
|
||||
#
|
||||
|
||||
class Datapath(object):
|
||||
"""Object encapsulating the actions necessary to (de)configure the
|
||||
datapath for a given PIF. Does not include configuration of the
|
||||
IP address on the ipdev.
|
||||
"""
|
||||
|
||||
def __init__(self, pif):
|
||||
self._pif = pif
|
||||
|
||||
@classmethod
|
||||
def rewrite(cls):
|
||||
"""Class method called when write action is called. Can be used
|
||||
to update any backend specific configuration."""
|
||||
pass
|
||||
|
||||
def configure_ipdev(self, cfg):
|
||||
"""Write ifcfg TYPE field for an IPdev, plus any type specific
|
||||
fields to cfg
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def preconfigure(self, parent):
|
||||
"""Prepare datapath configuration for PIF, but do not actually
|
||||
apply any changes.
|
||||
|
||||
Any configuration files should be attached to parent.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def bring_down_existing(self):
|
||||
"""Tear down any existing network device configuration which
|
||||
needs to be undone in order to bring this PIF up.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def configure(self):
|
||||
"""Apply the configuration prepared in the preconfigure stage.
|
||||
|
||||
Should assume any configuration files changed attached in
|
||||
the preconfigure stage are applied and bring up the
|
||||
necessary devices to provide the datapath for the
|
||||
PIF.
|
||||
|
||||
Should not bring up the IPdev.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def post(self):
|
||||
"""Called after the IPdev has been brought up.
|
||||
|
||||
Should do any final setup, including reinstating any
|
||||
devices which were taken down in the bring_down_existing
|
||||
hook.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def bring_down(self):
|
||||
"""Tear down and deconfigure the datapath. Should assume the
|
||||
IPdev has already been brought down.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def DatapathFactory():
|
||||
# XXX Need a datapath object for bridgeless PIFs
|
||||
|
||||
try:
|
||||
network_conf = open(root_prefix() + "/etc/xensource/network.conf", 'r')
|
||||
network_backend = network_conf.readline().strip()
|
||||
network_conf.close()
|
||||
except Exception as e:
|
||||
raise Error("failed to determine network backend:" + e)
|
||||
|
||||
if network_backend == "bridge":
|
||||
from InterfaceReconfigureBridge import DatapathBridge
|
||||
return DatapathBridge
|
||||
elif network_backend in ["openvswitch", "vswitch"]:
|
||||
from InterfaceReconfigureVswitch import DatapathVswitch
|
||||
return DatapathVswitch
|
||||
else:
|
||||
raise Error("unknown network backend %s" % network_backend)
|
@@ -1,476 +0,0 @@
|
||||
# Copyright (c) 2008,2009 Citrix Systems, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation; version 2.1 only. with the special
|
||||
# exception on linking described in file LICENSE.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
from InterfaceReconfigure import *
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
sysfs_bonding_masters = root_prefix() + "/sys/class/net/bonding_masters"
|
||||
|
||||
def open_pif_ifcfg(pif):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
interface = pif_netdev_name(pif)
|
||||
log("Configuring %s (%s)" % (interface, pifrec['MAC']))
|
||||
|
||||
f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), interface))
|
||||
|
||||
f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
|
||||
(os.path.basename(f.path()), os.path.basename(sys.argv[0])))
|
||||
f.write("XEMANAGED=yes\n")
|
||||
f.write("DEVICE=%s\n" % interface)
|
||||
f.write("ONBOOT=no\n")
|
||||
|
||||
return f
|
||||
|
||||
#
|
||||
# Bare Network Devices -- network devices without IP configuration
|
||||
#
|
||||
|
||||
def netdev_down(netdev):
|
||||
"""Bring down a bare network device"""
|
||||
if not netdev_exists(netdev):
|
||||
log("netdev: down: device %s does not exist, ignoring" % netdev)
|
||||
return
|
||||
run_command(["/sbin/ifdown", netdev])
|
||||
|
||||
def netdev_up(netdev, mtu=None):
|
||||
"""Bring up a bare network device"""
|
||||
#if not netdev_exists(netdev):
|
||||
# raise Error("netdev: up: device %s does not exist" % netdev)
|
||||
|
||||
run_command(["/sbin/ifup", netdev])
|
||||
|
||||
#
|
||||
# Bonding driver
|
||||
#
|
||||
|
||||
def load_bonding_driver():
|
||||
log("Loading bonding driver")
|
||||
run_command(["/sbin/modprobe", "bonding"])
|
||||
try:
|
||||
# bond_device_exists() uses the contents of sysfs_bonding_masters to work out which devices
|
||||
# have already been created. Unfortunately the driver creates "bond0" automatically at
|
||||
# modprobe init. Get rid of this now or our accounting will go wrong.
|
||||
f = open(sysfs_bonding_masters, "w")
|
||||
f.write("-bond0")
|
||||
f.close()
|
||||
except IOError as e:
|
||||
log("Failed to load bonding driver: %s" % e)
|
||||
|
||||
def bonding_driver_loaded():
|
||||
lines = open(root_prefix() + "/proc/modules").read().split("\n")
|
||||
modules = [line.split(" ")[0] for line in lines]
|
||||
return "bonding" in modules
|
||||
|
||||
def bond_device_exists(name):
|
||||
f = open(sysfs_bonding_masters, "r")
|
||||
bonds = f.readline().split()
|
||||
f.close()
|
||||
return name in bonds
|
||||
|
||||
def __create_bond_device(name):
|
||||
|
||||
if not bonding_driver_loaded():
|
||||
load_bonding_driver()
|
||||
|
||||
if bond_device_exists(name):
|
||||
log("bond master %s already exists, not creating" % name)
|
||||
else:
|
||||
log("Creating bond master %s" % name)
|
||||
try:
|
||||
f = open(sysfs_bonding_masters, "w")
|
||||
f.write("+" + name)
|
||||
f.close()
|
||||
except IOError as e:
|
||||
log("Failed to create %s: %s" % (name, e))
|
||||
|
||||
def create_bond_device(pif):
|
||||
"""Ensures that a bond master device exists in the kernel."""
|
||||
|
||||
if not pif_is_bond(pif):
|
||||
return
|
||||
|
||||
__create_bond_device(pif_netdev_name(pif))
|
||||
|
||||
def __destroy_bond_device(name):
|
||||
if bond_device_exists(name):
|
||||
retries = 10 # 10 * 0.5 seconds
|
||||
while retries > 0:
|
||||
retries = retries - 1
|
||||
log("Destroying bond master %s (%d attempts remain)" % (name,retries))
|
||||
try:
|
||||
f = open(sysfs_bonding_masters, "w")
|
||||
f.write("-" + name)
|
||||
f.close()
|
||||
retries = 0
|
||||
except IOError as e:
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
log("bond master %s does not exist, not destroying" % name)
|
||||
|
||||
def destroy_bond_device(pif):
|
||||
"""No, Mr. Bond, I expect you to die."""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
if not pif_is_bond(pif):
|
||||
return
|
||||
|
||||
# If the bonding module isn't loaded then do nothing.
|
||||
if not os.access(sysfs_bonding_masters, os.F_OK):
|
||||
return
|
||||
|
||||
name = pif_netdev_name(pif)
|
||||
|
||||
__destroy_bond_device(name)
|
||||
|
||||
#
|
||||
# Bring Interface up/down.
|
||||
#
|
||||
|
||||
def bring_down_interface(pif, destroy=False):
|
||||
"""Bring down the interface associated with PIF.
|
||||
|
||||
Brings down the given interface as well as any physical interfaces
|
||||
which are bond slaves of this one. This is because they will be
|
||||
required when the bond is brought up."""
|
||||
|
||||
def destroy_bridge(pif):
|
||||
"""Bring down the bridge associated with a PIF."""
|
||||
#if not pif_is_bridged(pif):
|
||||
# return
|
||||
bridge = pif_bridge_name(pif)
|
||||
if not netdev_exists(bridge):
|
||||
log("destroy_bridge: bridge %s does not exist, ignoring" % bridge)
|
||||
return
|
||||
log("Destroy bridge %s" % bridge)
|
||||
netdev_down(bridge)
|
||||
run_command(["/usr/sbin/brctl", "delbr", bridge])
|
||||
|
||||
def destroy_vlan(pif):
|
||||
vlan = pif_netdev_name(pif)
|
||||
if not netdev_exists(vlan):
|
||||
log("vconfig del: vlan %s does not exist, ignoring" % vlan)
|
||||
return
|
||||
log("Destroy vlan device %s" % vlan)
|
||||
run_command(["/sbin/vconfig", "rem", vlan])
|
||||
|
||||
if pif_is_vlan(pif):
|
||||
interface = pif_netdev_name(pif)
|
||||
log("bring_down_interface: %s is a VLAN" % interface)
|
||||
netdev_down(interface)
|
||||
|
||||
if destroy:
|
||||
destroy_vlan(pif)
|
||||
destroy_bridge(pif)
|
||||
else:
|
||||
return
|
||||
|
||||
slave = pif_get_vlan_slave(pif)
|
||||
if db().get_pif_record(slave)['currently_attached']:
|
||||
log("bring_down_interface: vlan slave is currently attached")
|
||||
return
|
||||
|
||||
masters = pif_get_vlan_masters(slave)
|
||||
masters = [m for m in masters if m != pif and db().get_pif_record(m)['currently_attached']]
|
||||
if len(masters) > 0:
|
||||
log("bring_down_interface: vlan slave has other masters")
|
||||
return
|
||||
|
||||
log("bring_down_interface: no more masters, bring down vlan slave %s" % pif_netdev_name(slave))
|
||||
pif = slave
|
||||
else:
|
||||
vlan_masters = pif_get_vlan_masters(pif)
|
||||
log("vlan masters of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(m) for m in vlan_masters]))
|
||||
if len([m for m in vlan_masters if db().get_pif_record(m)['currently_attached']]) > 0:
|
||||
log("Leaving %s up due to currently attached VLAN masters" % pif_netdev_name(pif))
|
||||
return
|
||||
|
||||
# pif is now either a bond or a physical device which needs to be brought down
|
||||
|
||||
# Need to bring down bond slaves first since the bond device
|
||||
# must be up to enslave/unenslave.
|
||||
bond_slaves = pif_get_bond_slaves_sorted(pif)
|
||||
log("bond slaves of %s - %s" % (db().get_pif_record(pif)['device'], [pif_netdev_name(s) for s in bond_slaves]))
|
||||
for slave in bond_slaves:
|
||||
slave_interface = pif_netdev_name(slave)
|
||||
if db().get_pif_record(slave)['currently_attached']:
|
||||
log("leave bond slave %s up (currently attached)" % slave_interface)
|
||||
continue
|
||||
log("bring down bond slave %s" % slave_interface)
|
||||
netdev_down(slave_interface)
|
||||
# Also destroy the bridge associated with the slave, since
|
||||
# it will carry the MAC address and possibly an IP address
|
||||
# leading to confusion.
|
||||
destroy_bridge(slave)
|
||||
|
||||
interface = pif_netdev_name(pif)
|
||||
log("Bring interface %s down" % interface)
|
||||
netdev_down(interface)
|
||||
|
||||
if destroy:
|
||||
destroy_bond_device(pif)
|
||||
destroy_bridge(pif)
|
||||
|
||||
def interface_is_up(pif):
|
||||
try:
|
||||
interface = pif_netdev_name(pif)
|
||||
state = open("%s/sys/class/net/%s/operstate" % (root_prefix(), interface)).read().strip()
|
||||
return state == "up"
|
||||
except:
|
||||
return False # interface prolly doesn't exist
|
||||
|
||||
def bring_up_interface(pif):
|
||||
"""Bring up the interface associated with a PIF.
|
||||
|
||||
Also bring up the interfaces listed in additional.
|
||||
"""
|
||||
|
||||
# VLAN on bond seems to need bond brought up explicitly, but VLAN
|
||||
# on normal device does not. Might as well always bring it up.
|
||||
if pif_is_vlan(pif):
|
||||
slave = pif_get_vlan_slave(pif)
|
||||
if not interface_is_up(slave):
|
||||
bring_up_interface(slave)
|
||||
|
||||
interface = pif_netdev_name(pif)
|
||||
|
||||
create_bond_device(pif)
|
||||
|
||||
log("Bring interface %s up" % interface)
|
||||
netdev_up(interface)
|
||||
|
||||
|
||||
#
|
||||
# Datapath topology configuration.
|
||||
#
|
||||
|
||||
def _configure_physical_interface(pif):
|
||||
"""Write the configuration for a physical interface.
|
||||
|
||||
Writes the configuration file for the physical interface described by
|
||||
the pif object.
|
||||
|
||||
Returns the open file handle for the interface configuration file.
|
||||
"""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
log("Configuring physical interface %s" % pifrec['device'])
|
||||
|
||||
f = open_pif_ifcfg(pif)
|
||||
|
||||
f.write("TYPE=Ethernet\n")
|
||||
f.write("HWADDR=%(MAC)s\n" % pifrec)
|
||||
|
||||
settings,offload = ethtool_settings(pifrec['other_config'],
|
||||
PIF_OTHERCONFIG_DEFAULTS)
|
||||
if len(settings):
|
||||
f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
|
||||
if len(offload):
|
||||
f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
|
||||
|
||||
mtu = mtu_setting(pifrec['network'], "PIF", pifrec['other_config'])
|
||||
if mtu:
|
||||
f.write("MTU=%s\n" % mtu)
|
||||
|
||||
return f
|
||||
|
||||
def pif_get_bond_slaves_sorted(pif):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
# build a list of slave's pifs
|
||||
slave_pifs = pif_get_bond_slaves(pif)
|
||||
|
||||
# Ensure any currently attached slaves are listed in the opposite order to the order in
|
||||
# which they were attached. The first slave attached must be the last detached since
|
||||
# the bond is using its MAC address.
|
||||
try:
|
||||
attached_slaves = open("%s/sys/class/net/%s/bonding/slaves" % (root_prefix(), pifrec['device'])).readline().split()
|
||||
for slave in attached_slaves:
|
||||
pifs = [p for p in db().get_pifs_by_device(slave) if not pif_is_vlan(p)]
|
||||
slave_pif = pifs[0]
|
||||
slave_pifs.remove(slave_pif)
|
||||
slave_pifs.insert(0, slave_pif)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
return slave_pifs
|
||||
|
||||
def _configure_bond_interface(pif):
|
||||
"""Write the configuration for a bond interface.
|
||||
|
||||
Writes the configuration file for the bond interface described by
|
||||
the pif object. Handles writing the configuration for the slave
|
||||
interfaces.
|
||||
|
||||
Returns the open file handle for the bond interface configuration
|
||||
file.
|
||||
"""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
f = open_pif_ifcfg(pif)
|
||||
|
||||
if pifrec['MAC'] != "":
|
||||
f.write("MACADDR=%s\n" % pifrec['MAC'])
|
||||
|
||||
for slave in pif_get_bond_slaves(pif):
|
||||
s = _configure_physical_interface(slave)
|
||||
s.write("MASTER=%(device)s\n" % pifrec)
|
||||
s.write("SLAVE=yes\n")
|
||||
s.close()
|
||||
f.attach_child(s)
|
||||
|
||||
settings,offload = ethtool_settings(pifrec['other_config'])
|
||||
if len(settings):
|
||||
f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
|
||||
if len(offload):
|
||||
f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
|
||||
|
||||
mtu = mtu_setting(pifrec['network'], "Bond-PIF", pifrec['other_config'])
|
||||
if mtu:
|
||||
f.write("MTU=%s\n" % mtu)
|
||||
|
||||
# The bond option defaults
|
||||
bond_options = {
|
||||
"mode": "balance-slb",
|
||||
"miimon": "100",
|
||||
"downdelay": "200",
|
||||
"updelay": "31000",
|
||||
"use_carrier": "1",
|
||||
"hashing-algorithm": "src_mac",
|
||||
}
|
||||
|
||||
# override defaults with values from other-config whose keys being with "bond-"
|
||||
oc = pifrec['other_config']
|
||||
overrides = filter(lambda key,val: key.startswith("bond-"), oc.items())
|
||||
overrides = map(lambda key,val: (key[5:], val), overrides)
|
||||
bond_options.update(overrides)
|
||||
|
||||
# write the bond options to ifcfg-bondX
|
||||
f.write('BONDING_OPTS="')
|
||||
for (name,val) in bond_options.items():
|
||||
f.write("%s=%s " % (name,val))
|
||||
f.write('"\n')
|
||||
return f
|
||||
|
||||
def _configure_vlan_interface(pif):
|
||||
"""Write the configuration for a VLAN interface.
|
||||
|
||||
Writes the configuration file for the VLAN interface described by
|
||||
the pif object. Handles writing the configuration for the master
|
||||
interface if necessary.
|
||||
|
||||
Returns the open file handle for the VLAN interface configuration
|
||||
file.
|
||||
"""
|
||||
|
||||
slave = _configure_pif(pif_get_vlan_slave(pif))
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
f = open_pif_ifcfg(pif)
|
||||
f.write("VLAN=yes\n")
|
||||
|
||||
settings,offload = ethtool_settings(pifrec['other_config'])
|
||||
if len(settings):
|
||||
f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
|
||||
if len(offload):
|
||||
f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
|
||||
|
||||
mtu = mtu_setting(pifrec['network'], "VLAN-PIF", pifrec['other_config'])
|
||||
if mtu:
|
||||
f.write("MTU=%s\n" % mtu)
|
||||
|
||||
f.attach_child(slave)
|
||||
|
||||
return f
|
||||
|
||||
def _configure_pif(pif):
|
||||
"""Write the configuration for a PIF object.
|
||||
|
||||
Writes the configuration file the PIF and all dependent
|
||||
interfaces (bond slaves and VLAN masters etc).
|
||||
|
||||
Returns the open file handle for the interface configuration file.
|
||||
"""
|
||||
|
||||
if pif_is_vlan(pif):
|
||||
f = _configure_vlan_interface(pif)
|
||||
elif pif_is_bond(pif):
|
||||
f = _configure_bond_interface(pif)
|
||||
else:
|
||||
f = _configure_physical_interface(pif)
|
||||
|
||||
f.write("BRIDGE=%s\n" % pif_bridge_name(pif))
|
||||
f.close()
|
||||
|
||||
return f
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
class DatapathBridge(Datapath):
|
||||
def __init__(self, pif):
|
||||
if pif_is_tunnel(pif):
|
||||
raise Error("Tunnel PIFs are not supported in Bridge mode")
|
||||
|
||||
Datapath.__init__(self, pif)
|
||||
log("Configured for Bridge datapath")
|
||||
|
||||
def configure_ipdev(self, cfg):
|
||||
if pif_is_bridged(self._pif):
|
||||
cfg.write("TYPE=Bridge\n")
|
||||
cfg.write("DELAY=0\n")
|
||||
cfg.write("STP=off\n")
|
||||
cfg.write("PIFDEV=%s\n" % pif_netdev_name(self._pif))
|
||||
else:
|
||||
cfg.write("TYPE=Ethernet\n")
|
||||
|
||||
def preconfigure(self, parent):
|
||||
pf = _configure_pif(self._pif)
|
||||
parent.attach_child(pf)
|
||||
|
||||
def bring_down_existing(self):
|
||||
# Bring down any VLAN masters so that we can reconfigure the slave.
|
||||
for master in pif_get_vlan_masters(self._pif):
|
||||
name = pif_netdev_name(master)
|
||||
log("action_up: bring down vlan master %s" % (name))
|
||||
netdev_down(name)
|
||||
|
||||
# interface-reconfigure is never explicitly called to down a bond master.
|
||||
# However, when we are called to up a slave it is implicit that we are destroying the master.
|
||||
bond_masters = pif_get_bond_masters(self._pif)
|
||||
for master in bond_masters:
|
||||
log("action_up: bring down bond master %s" % (pif_netdev_name(master)))
|
||||
# bring down master
|
||||
bring_down_interface(master, destroy=True)
|
||||
|
||||
# No masters left - now its safe to reconfigure the slave.
|
||||
bring_down_interface(self._pif)
|
||||
|
||||
def configure(self):
|
||||
bring_up_interface(self._pif)
|
||||
|
||||
def post(self):
|
||||
# Bring back any currently-attached VLAN masters
|
||||
for master in [v for v in pif_get_vlan_masters(self._pif) if db().get_pif_record(v)['currently_attached']]:
|
||||
name = pif_netdev_name(master)
|
||||
log("action_up: bring up %s" % (name))
|
||||
netdev_up(name)
|
||||
|
||||
def bring_down(self):
|
||||
bring_down_interface(self._pif, destroy=True)
|
@@ -1,724 +0,0 @@
|
||||
# Copyright (c) 2008,2009,2011 Citrix Systems, Inc.
|
||||
# Copyright (c) 2009,2010,2011,2012,2013,2017 Nicira, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation; version 2.1 only. with the special
|
||||
# exception on linking described in file LICENSE.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
from InterfaceReconfigure import *
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
|
||||
#
|
||||
# Bare Network Devices -- network devices without IP configuration
|
||||
#
|
||||
|
||||
def netdev_down(netdev):
|
||||
"""Bring down a bare network device"""
|
||||
if not netdev_exists(netdev):
|
||||
log("netdev: down: device %s does not exist, ignoring" % netdev)
|
||||
return
|
||||
run_command(["/sbin/ip", "link", "set", netdev, 'down'])
|
||||
|
||||
def netdev_up(netdev, mtu=None):
|
||||
"""Bring up a bare network device"""
|
||||
if not netdev_exists(netdev):
|
||||
raise Error("netdev: up: device %s does not exist" % netdev)
|
||||
|
||||
if mtu:
|
||||
mtu = ["mtu", mtu]
|
||||
else:
|
||||
mtu = []
|
||||
|
||||
run_command(["/sbin/ip", "link", "set", netdev, 'up'] + mtu)
|
||||
|
||||
# This is a list of drivers that do support VLAN tx or rx acceleration, but
|
||||
# to which the VLAN bug workaround should not be applied. This could be
|
||||
# because these are known-good drivers (that is, they do not have any of
|
||||
# the bugs that the workaround avoids) or because the VLAN bug workaround
|
||||
# will not work for them and may cause other problems.
|
||||
#
|
||||
# This is a very short list because few drivers have been tested.
|
||||
NO_VLAN_WORKAROUND_DRIVERS = (
|
||||
"bonding",
|
||||
)
|
||||
def netdev_get_driver_name(netdev):
|
||||
"""Returns the name of the driver for network device 'netdev'"""
|
||||
symlink = '%s/sys/class/net/%s/device/driver' % (root_prefix(), netdev)
|
||||
try:
|
||||
target = os.readlink(symlink)
|
||||
except OSError as e:
|
||||
log("%s: could not read netdev's driver name (%s)" % (netdev, e))
|
||||
return None
|
||||
|
||||
slash = target.rfind('/')
|
||||
if slash < 0:
|
||||
log("target %s of symbolic link %s does not contain slash"
|
||||
% (target, symlink))
|
||||
return None
|
||||
|
||||
return target[slash + 1:]
|
||||
|
||||
def netdev_get_features(netdev):
|
||||
"""Returns the features bitmap for the driver for 'netdev'.
|
||||
The features bitmap is a set of NETIF_F_ flags supported by its driver."""
|
||||
try:
|
||||
features = open("%s/sys/class/net/%s/features" % (root_prefix(), netdev)).read().strip()
|
||||
return int(features, 0)
|
||||
except:
|
||||
return 0 # interface prolly doesn't exist
|
||||
|
||||
def netdev_has_vlan_accel(netdev):
|
||||
"""Returns True if 'netdev' supports VLAN acceleration, False otherwise."""
|
||||
NETIF_F_HW_VLAN_TX = 128
|
||||
NETIF_F_HW_VLAN_RX = 256
|
||||
NETIF_F_VLAN = NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX
|
||||
return (netdev_get_features(netdev) & NETIF_F_VLAN) != 0
|
||||
|
||||
#
|
||||
# PIF miscellanea
|
||||
#
|
||||
|
||||
def pif_currently_in_use(pif):
|
||||
"""Determine if a PIF is currently in use.
|
||||
|
||||
A PIF is determined to be currently in use if
|
||||
- PIF.currently-attached is true
|
||||
- Any bond master is currently attached
|
||||
- Any VLAN master is currently attached
|
||||
"""
|
||||
rec = db().get_pif_record(pif)
|
||||
if rec['currently_attached']:
|
||||
log("configure_datapath: %s is currently attached" % (pif_netdev_name(pif)))
|
||||
return True
|
||||
for b in pif_get_bond_masters(pif):
|
||||
if pif_currently_in_use(b):
|
||||
log("configure_datapath: %s is in use by BOND master %s" % (pif_netdev_name(pif),pif_netdev_name(b)))
|
||||
return True
|
||||
for v in pif_get_vlan_masters(pif):
|
||||
if pif_currently_in_use(v):
|
||||
log("configure_datapath: %s is in use by VLAN master %s" % (pif_netdev_name(pif),pif_netdev_name(v)))
|
||||
return True
|
||||
return False
|
||||
|
||||
#
|
||||
# Datapath Configuration
|
||||
#
|
||||
|
||||
def pif_datapath(pif):
|
||||
"""Return the datapath PIF associated with PIF.
|
||||
A non-VLAN PIF is its own datapath PIF, except that a bridgeless PIF has
|
||||
no datapath PIF at all.
|
||||
A VLAN PIF's datapath PIF is its VLAN slave's datapath PIF.
|
||||
"""
|
||||
if pif_is_vlan(pif):
|
||||
return pif_datapath(pif_get_vlan_slave(pif))
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
nwrec = db().get_network_record(pifrec['network'])
|
||||
if not nwrec['bridge']:
|
||||
return None
|
||||
else:
|
||||
return pif
|
||||
|
||||
def datapath_get_physical_pifs(pif):
|
||||
"""Return the PIFs for the physical network device(s) associated with a datapath PIF.
|
||||
For a bond master PIF, these are the bond slave PIFs.
|
||||
For a non-VLAN, non-bond master PIF, the PIF is its own physical device PIF.
|
||||
|
||||
A VLAN PIF cannot be a datapath PIF.
|
||||
"""
|
||||
if pif_is_tunnel(pif):
|
||||
return []
|
||||
elif pif_is_vlan(pif):
|
||||
# Seems like overkill...
|
||||
raise Error("get-physical-pifs should not get passed a VLAN")
|
||||
elif pif_is_bond(pif):
|
||||
return pif_get_bond_slaves(pif)
|
||||
else:
|
||||
return [pif]
|
||||
|
||||
def datapath_deconfigure_physical(netdev):
|
||||
return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
|
||||
|
||||
def vsctl_escape(s):
|
||||
if s.isalnum():
|
||||
return s
|
||||
|
||||
def escape(match):
|
||||
c = match.group(0)
|
||||
if c == '\0':
|
||||
raise Error("strings may not contain null bytes")
|
||||
elif c == '\\':
|
||||
return r'\\'
|
||||
elif c == '\n':
|
||||
return r'\n'
|
||||
elif c == '\r':
|
||||
return r'\r'
|
||||
elif c == '\t':
|
||||
return r'\t'
|
||||
elif c == '\b':
|
||||
return r'\b'
|
||||
elif c == '\a':
|
||||
return r'\a'
|
||||
else:
|
||||
return r'\x%02x' % ord(c)
|
||||
return '"' + re.sub(r'["\\\000-\037]', escape, s) + '"'
|
||||
|
||||
def datapath_configure_tunnel(pif):
|
||||
pass
|
||||
|
||||
def datapath_configure_bond(pif,slaves):
|
||||
bridge = pif_bridge_name(pif)
|
||||
pifrec = db().get_pif_record(pif)
|
||||
interface = pif_netdev_name(pif)
|
||||
|
||||
argv = ['--', '--fake-iface', 'add-bond', bridge, interface]
|
||||
for slave in slaves:
|
||||
argv += [pif_netdev_name(slave)]
|
||||
|
||||
# Bonding options.
|
||||
bond_options = {
|
||||
"mode": "balance-slb",
|
||||
"miimon": "100",
|
||||
"downdelay": "200",
|
||||
"updelay": "31000",
|
||||
"use_carrier": "1",
|
||||
"hashing-algorithm": "src_mac",
|
||||
}
|
||||
# override defaults with values from other-config whose keys
|
||||
# being with "bond-"
|
||||
oc = pifrec['other_config']
|
||||
overrides = filter(lambda key_val:
|
||||
key_val[0].startswith("bond-"), oc.items())
|
||||
overrides = map(lambda key_val: (key_val[0][5:], key_val[1]), overrides)
|
||||
bond_options.update(overrides)
|
||||
mode = None
|
||||
halgo = None
|
||||
|
||||
argv += ['--', 'set', 'Port', interface]
|
||||
if pifrec['MAC'] != "":
|
||||
argv += ['MAC=%s' % vsctl_escape(pifrec['MAC'])]
|
||||
for (name,val) in sorted(bond_options.items()):
|
||||
if name in ['updelay', 'downdelay']:
|
||||
# updelay and downdelay have dedicated schema columns.
|
||||
# The value must be a nonnegative integer.
|
||||
try:
|
||||
value = int(val)
|
||||
if value < 0:
|
||||
raise ValueError
|
||||
|
||||
argv += ['bond_%s=%d' % (name, value)]
|
||||
except ValueError:
|
||||
log("bridge %s has invalid %s '%s'" % (bridge, name, value))
|
||||
elif name in ['miimon', 'use_carrier']:
|
||||
try:
|
||||
value = int(val)
|
||||
if value < 0:
|
||||
raise ValueError
|
||||
|
||||
if name == 'use_carrier':
|
||||
if value:
|
||||
value = "carrier"
|
||||
else:
|
||||
value = "miimon"
|
||||
argv += ["other-config:bond-detect-mode=%s" % value]
|
||||
else:
|
||||
argv += ["other-config:bond-miimon-interval=%d" % value]
|
||||
except ValueError:
|
||||
log("bridge %s has invalid %s '%s'" % (bridge, name, value))
|
||||
elif name == "mode":
|
||||
mode = val
|
||||
elif name == "hashing-algorithm":
|
||||
halgo = val
|
||||
else:
|
||||
# Pass other bond options into other_config.
|
||||
argv += ["other-config:%s=%s" % (vsctl_escape("bond-%s" % name),
|
||||
vsctl_escape(val))]
|
||||
|
||||
if mode == 'lacp':
|
||||
argv += ['lacp=active']
|
||||
|
||||
if halgo == 'src_mac':
|
||||
argv += ['bond_mode=balance-slb']
|
||||
elif halgo == "tcpudp_ports":
|
||||
argv += ['bond_mode=balance-tcp']
|
||||
else:
|
||||
log("bridge %s has invalid bond-hashing-algorithm '%s'" % (bridge, halgo))
|
||||
argv += ['bond_mode=balance-slb']
|
||||
elif mode in ['balance-slb', 'active-backup']:
|
||||
argv += ['lacp=off', 'bond_mode=%s' % mode]
|
||||
else:
|
||||
log("bridge %s has invalid bond-mode '%s'" % (bridge, mode))
|
||||
argv += ['lacp=off', 'bond_mode=balance-slb']
|
||||
|
||||
return argv
|
||||
|
||||
def datapath_deconfigure_bond(netdev):
|
||||
return ['--', '--with-iface', '--if-exists', 'del-port', netdev]
|
||||
|
||||
def datapath_deconfigure_ipdev(interface):
|
||||
return ['--', '--with-iface', '--if-exists', 'del-port', interface]
|
||||
|
||||
def datapath_modify_config(commands):
|
||||
#log("modifying configuration:")
|
||||
#for c in commands:
|
||||
# log(" %s" % c)
|
||||
|
||||
rc = run_command(['/usr/bin/ovs-vsctl'] + ['--timeout=20']
|
||||
+ [c for c in commands if not c.startswith('#')])
|
||||
if not rc:
|
||||
raise Error("Failed to modify vswitch configuration")
|
||||
return True
|
||||
|
||||
#
|
||||
# Toplevel Datapath Configuration.
|
||||
#
|
||||
|
||||
def configure_datapath(pif):
|
||||
"""Bring up the configuration for 'pif', which must not be a VLAN PIF, by:
|
||||
- Tearing down other PIFs that use the same physical devices as 'pif'.
|
||||
- Ensuring that 'pif' itself is set up.
|
||||
- *Not* tearing down any PIFs that are stacked on top of 'pif' (i.e. VLANs
|
||||
on top of 'pif'.
|
||||
|
||||
Returns a tuple containing
|
||||
- A list containing the necessary vsctl command line arguments
|
||||
- A list of additional devices which should be brought up after
|
||||
the configuration is applied.
|
||||
- A list containing flows to apply to the pif bridge, note that
|
||||
port numbers may need to be substituted once ofport is known
|
||||
"""
|
||||
|
||||
vsctl_argv = []
|
||||
extra_up_ports = []
|
||||
bridge_flows = []
|
||||
|
||||
assert not pif_is_vlan(pif)
|
||||
bridge = pif_bridge_name(pif)
|
||||
|
||||
physical_devices = datapath_get_physical_pifs(pif)
|
||||
|
||||
vsctl_argv += ['## configuring datapath %s' % bridge]
|
||||
|
||||
# Determine additional devices to deconfigure.
|
||||
#
|
||||
# Given all physical devices which are part of this PIF we need to
|
||||
# consider:
|
||||
# - any additional bond which a physical device is part of.
|
||||
# - any additional physical devices which are part of an additional bond.
|
||||
#
|
||||
# Any of these which are not currently in use should be brought
|
||||
# down and deconfigured.
|
||||
extra_down_bonds = []
|
||||
extra_down_ports = []
|
||||
for p in physical_devices:
|
||||
for bond in pif_get_bond_masters(p):
|
||||
if bond == pif:
|
||||
log("configure_datapath: leaving bond %s up" % pif_netdev_name(bond))
|
||||
continue
|
||||
if bond in extra_down_bonds:
|
||||
continue
|
||||
if db().get_pif_record(bond)['currently_attached']:
|
||||
log("configure_datapath: implicitly tearing down currently-attached bond %s" % pif_netdev_name(bond))
|
||||
|
||||
extra_down_bonds += [bond]
|
||||
|
||||
for s in pif_get_bond_slaves(bond):
|
||||
if s in physical_devices:
|
||||
continue
|
||||
if s in extra_down_ports:
|
||||
continue
|
||||
if pif_currently_in_use(s):
|
||||
continue
|
||||
extra_down_ports += [s]
|
||||
|
||||
log("configure_datapath: bridge - %s" % bridge)
|
||||
log("configure_datapath: physical - %s" % [pif_netdev_name(p) for p in physical_devices])
|
||||
log("configure_datapath: extra ports - %s" % [pif_netdev_name(p) for p in extra_down_ports])
|
||||
log("configure_datapath: extra bonds - %s" % [pif_netdev_name(p) for p in extra_down_bonds])
|
||||
|
||||
# Need to fully deconfigure any bridge which any of the:
|
||||
# - physical devices
|
||||
# - bond devices
|
||||
# - sibling devices
|
||||
# refers to
|
||||
for brpif in physical_devices + extra_down_ports + extra_down_bonds:
|
||||
if brpif == pif:
|
||||
continue
|
||||
b = pif_bridge_name(brpif)
|
||||
#ifdown(b)
|
||||
# XXX
|
||||
netdev_down(b)
|
||||
vsctl_argv += ['# remove bridge %s' % b]
|
||||
vsctl_argv += ['--', '--if-exists', 'del-br', b]
|
||||
|
||||
for n in extra_down_ports:
|
||||
dev = pif_netdev_name(n)
|
||||
vsctl_argv += ['# deconfigure sibling physical device %s' % dev]
|
||||
vsctl_argv += datapath_deconfigure_physical(dev)
|
||||
netdev_down(dev)
|
||||
|
||||
for n in extra_down_bonds:
|
||||
dev = pif_netdev_name(n)
|
||||
vsctl_argv += ['# deconfigure bond device %s' % dev]
|
||||
vsctl_argv += datapath_deconfigure_bond(dev)
|
||||
netdev_down(dev)
|
||||
|
||||
for p in physical_devices:
|
||||
dev = pif_netdev_name(p)
|
||||
vsctl_argv += ['# deconfigure physical port %s' % dev]
|
||||
vsctl_argv += datapath_deconfigure_physical(dev)
|
||||
|
||||
vsctl_argv += ['--', '--may-exist', 'add-br', bridge]
|
||||
|
||||
if len(physical_devices) > 1:
|
||||
vsctl_argv += ['# deconfigure bond %s' % pif_netdev_name(pif)]
|
||||
vsctl_argv += datapath_deconfigure_bond(pif_netdev_name(pif))
|
||||
vsctl_argv += ['# configure bond %s' % pif_netdev_name(pif)]
|
||||
vsctl_argv += datapath_configure_bond(pif, physical_devices)
|
||||
extra_up_ports += [pif_netdev_name(pif)]
|
||||
elif len(physical_devices) == 1:
|
||||
iface = pif_netdev_name(physical_devices[0])
|
||||
vsctl_argv += ['# add physical device %s' % iface]
|
||||
vsctl_argv += ['--', '--may-exist', 'add-port', bridge, iface]
|
||||
elif pif_is_tunnel(pif):
|
||||
datapath_configure_tunnel(pif)
|
||||
|
||||
vsctl_argv += ['# configure Bridge MAC']
|
||||
vsctl_argv += ['--', 'set', 'Bridge', bridge,
|
||||
'other-config:hwaddr=%s' % vsctl_escape(db().get_pif_record(pif)['MAC'])]
|
||||
|
||||
pool = db().get_pool_record()
|
||||
network = db().get_network_by_bridge(bridge)
|
||||
network_rec = None
|
||||
fail_mode = None
|
||||
valid_fail_modes = ['standalone', 'secure']
|
||||
|
||||
if network:
|
||||
network_rec = db().get_network_record(network)
|
||||
fail_mode = network_rec['other_config'].get('vswitch-controller-fail-mode')
|
||||
|
||||
if (fail_mode not in valid_fail_modes) and pool:
|
||||
fail_mode = pool['other_config'].get('vswitch-controller-fail-mode')
|
||||
# Add default flows to allow management traffic if fail-mode
|
||||
# transitions to secure based on pool fail-mode setting
|
||||
if fail_mode == 'secure' and db().get_pif_record(pif).get('management', False):
|
||||
prev_fail_mode = vswitchCfgQuery(['get-fail-mode', bridge])
|
||||
if prev_fail_mode != 'secure':
|
||||
tp = 'idle_timeout=0,priority=0'
|
||||
host_mgmt_mac = db().get_pif_record(pif)['MAC']
|
||||
# account for bond as management interface
|
||||
if len(physical_devices) > 1:
|
||||
bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)]
|
||||
bridge_flows += ['%s,in_port=local,dl_src=%s,actions=NORMAL' % (tp, host_mgmt_mac)]
|
||||
# we don't know slave ofports yet, substitute later
|
||||
bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)]
|
||||
bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)]
|
||||
else:
|
||||
bridge_flows += ['%s,in_port=%%s,arp,nw_proto=1,actions=local' % (tp)]
|
||||
bridge_flows += ['%s,in_port=local,arp,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)]
|
||||
bridge_flows += ['%s,in_port=%%s,dl_dst=%s,actions=local' % (tp, host_mgmt_mac)]
|
||||
bridge_flows += ['%s,in_port=local,dl_src=%s,actions=%%s' % (tp, host_mgmt_mac)]
|
||||
|
||||
if fail_mode not in valid_fail_modes:
|
||||
fail_mode = 'standalone'
|
||||
|
||||
vsctl_argv += ['--', 'set', 'Bridge', bridge, 'fail_mode=%s' % fail_mode]
|
||||
|
||||
if network_rec:
|
||||
dib = network_rec['other_config'].get('vswitch-disable-in-band')
|
||||
if not dib:
|
||||
vsctl_argv += ['--', 'remove', 'Bridge', bridge, 'other_config', 'disable-in-band']
|
||||
elif dib in ['true', 'false']:
|
||||
vsctl_argv += ['--', 'set', 'Bridge', bridge, 'other_config:disable-in-band=' + dib]
|
||||
else:
|
||||
log('"' + dib + '"' "isn't a valid setting for other_config:disable-in-band on " + bridge)
|
||||
|
||||
vsctl_argv += set_br_external_ids(pif)
|
||||
vsctl_argv += ['## done configuring datapath %s' % bridge]
|
||||
|
||||
return vsctl_argv,extra_up_ports,bridge_flows
|
||||
|
||||
def deconfigure_bridge(pif):
|
||||
vsctl_argv = []
|
||||
|
||||
bridge = pif_bridge_name(pif)
|
||||
|
||||
log("deconfigure_bridge: bridge - %s" % bridge)
|
||||
|
||||
vsctl_argv += ['# deconfigure bridge %s' % bridge]
|
||||
vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
|
||||
|
||||
return vsctl_argv
|
||||
|
||||
def set_br_external_ids(pif):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
dp = pif_datapath(pif)
|
||||
dprec = db().get_pif_record(dp)
|
||||
|
||||
xs_network_uuids = []
|
||||
for nwpif in db().get_pifs_by_device(pifrec['device']):
|
||||
rec = db().get_pif_record(nwpif)
|
||||
|
||||
# When state is read from dbcache PIF.currently_attached
|
||||
# is always assumed to be false... Err on the side of
|
||||
# listing even detached networks for the time being.
|
||||
#if nwpif != pif and not rec['currently_attached']:
|
||||
# log("Network PIF %s not currently attached (%s)" % (rec['uuid'],pifrec['uuid']))
|
||||
# continue
|
||||
nwrec = db().get_network_record(rec['network'])
|
||||
|
||||
uuid = nwrec['uuid']
|
||||
if pif_is_vlan(nwpif):
|
||||
xs_network_uuids.append(uuid)
|
||||
else:
|
||||
xs_network_uuids.insert(0, uuid)
|
||||
|
||||
vsctl_argv = []
|
||||
vsctl_argv += ['# configure xs-network-uuids']
|
||||
vsctl_argv += ['--', 'br-set-external-id', pif_bridge_name(pif),
|
||||
'xs-network-uuids', ';'.join(xs_network_uuids)]
|
||||
|
||||
return vsctl_argv
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
class DatapathVswitch(Datapath):
|
||||
def __init__(self, pif):
|
||||
Datapath.__init__(self, pif)
|
||||
self._dp = pif_datapath(pif)
|
||||
self._ipdev = pif_ipdev_name(pif)
|
||||
self._bridge_flows = []
|
||||
|
||||
if pif_is_vlan(pif) and not self._dp:
|
||||
raise Error("Unbridged VLAN devices not implemented yet")
|
||||
|
||||
log("Configured for Vswitch datapath")
|
||||
|
||||
@classmethod
|
||||
def rewrite(cls):
|
||||
if not os.path.exists("/var/run/openvswitch/db.sock"):
|
||||
# ovsdb-server is not running, so we can't update the database.
|
||||
# Probably we are being called as part of system shutdown. Just
|
||||
# skip the update, since the external-ids will be updated on the
|
||||
# next boot anyhow.
|
||||
return
|
||||
|
||||
vsctl_argv = []
|
||||
for pif in db().get_all_pifs():
|
||||
pifrec = db().get_pif_record(pif)
|
||||
if not pif_is_vlan(pif) and pifrec['currently_attached']:
|
||||
vsctl_argv += set_br_external_ids(pif)
|
||||
|
||||
if vsctl_argv != []:
|
||||
datapath_modify_config(vsctl_argv)
|
||||
|
||||
def configure_ipdev(self, cfg):
|
||||
cfg.write("TYPE=Ethernet\n")
|
||||
|
||||
def preconfigure(self, parent):
|
||||
vsctl_argv = []
|
||||
extra_ports = []
|
||||
bridge_flows = []
|
||||
|
||||
pifrec = db().get_pif_record(self._pif)
|
||||
dprec = db().get_pif_record(self._dp)
|
||||
|
||||
ipdev = self._ipdev
|
||||
c,e,f = configure_datapath(self._dp)
|
||||
bridge = pif_bridge_name(self._pif)
|
||||
vsctl_argv += c
|
||||
extra_ports += e
|
||||
bridge_flows += f
|
||||
|
||||
dpname = pif_bridge_name(self._dp)
|
||||
|
||||
if pif_is_vlan(self._pif):
|
||||
# In some cases XAPI may misguidedly leave an instance of
|
||||
# 'bridge' which should be deleted.
|
||||
vsctl_argv += ['--', '--if-exists', 'del-br', bridge]
|
||||
|
||||
# configure_datapath() set up the underlying datapath bridge.
|
||||
# Stack a VLAN bridge on top of it.
|
||||
vsctl_argv += ['--', '--may-exist', 'add-br',
|
||||
bridge, dpname, pifrec['VLAN']]
|
||||
|
||||
vsctl_argv += set_br_external_ids(self._pif)
|
||||
|
||||
if ipdev != bridge:
|
||||
vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
|
||||
vsctl_argv += datapath_deconfigure_ipdev(ipdev)
|
||||
vsctl_argv += ["# reconfigure ipdev %s" % ipdev]
|
||||
vsctl_argv += ['--', 'add-port', bridge, ipdev]
|
||||
|
||||
if ipdev != dpname:
|
||||
vsctl_argv += ['# configure Interface MAC']
|
||||
vsctl_argv += ['--', 'set', 'Interface', pif_ipdev_name(self._pif),
|
||||
'MAC=%s' % vsctl_escape(dprec['MAC'])]
|
||||
|
||||
self._vsctl_argv = vsctl_argv
|
||||
self._extra_ports = extra_ports
|
||||
self._bridge_flows = bridge_flows
|
||||
|
||||
def bring_down_existing(self):
|
||||
# interface-reconfigure is never explicitly called to down a
|
||||
# bond master. However, when we are called to up a slave it
|
||||
# is implicit that we are destroying the master. Conversely,
|
||||
# when we are called to up a bond is is implicit that we are
|
||||
# taking down the slaves.
|
||||
#
|
||||
# This is (only) important in the case where the device being
|
||||
# implicitly taken down uses DHCP. We need to kill the
|
||||
# dhclient process, otherwise performing the inverse operation
|
||||
# later later will fail because ifup will refuse to start a
|
||||
# duplicate dhclient.
|
||||
bond_masters = pif_get_bond_masters(self._pif)
|
||||
for master in bond_masters:
|
||||
log("action_up: bring down bond master %s" % (pif_netdev_name(master)))
|
||||
run_command(["/sbin/ifdown", pif_bridge_name(master)])
|
||||
|
||||
bond_slaves = pif_get_bond_slaves(self._pif)
|
||||
for slave in bond_slaves:
|
||||
log("action_up: bring down bond slave %s" % (pif_netdev_name(slave)))
|
||||
run_command(["/sbin/ifdown", pif_bridge_name(slave)])
|
||||
|
||||
def configure(self):
|
||||
# Bring up physical devices. ovs-vswitchd initially enables or
|
||||
# disables bond slaves based on whether carrier is detected
|
||||
# when they are added, and a network device that is down
|
||||
# always reports "no carrier".
|
||||
physical_devices = datapath_get_physical_pifs(self._dp)
|
||||
|
||||
if pif_is_bond(self._dp):
|
||||
brec = db().get_pif_record(self._dp)
|
||||
bond_mtu = mtu_setting(brec['network'], "PIF", brec['other_config'])
|
||||
else:
|
||||
bond_mtu = None
|
||||
|
||||
for p in physical_devices:
|
||||
prec = db().get_pif_record(p)
|
||||
oc = prec['other_config']
|
||||
|
||||
dev = pif_netdev_name(p)
|
||||
|
||||
if bond_mtu:
|
||||
mtu = bond_mtu
|
||||
else:
|
||||
mtu = mtu_setting(prec['network'], "PIF", oc)
|
||||
|
||||
netdev_up(dev, mtu)
|
||||
|
||||
settings, offload = ethtool_settings(oc, PIF_OTHERCONFIG_DEFAULTS)
|
||||
if len(settings):
|
||||
run_command(['/sbin/ethtool', '-s', dev] + settings)
|
||||
if len(offload):
|
||||
run_command(['/sbin/ethtool', '-K', dev] + offload)
|
||||
|
||||
driver = netdev_get_driver_name(dev)
|
||||
if 'vlan-bug-workaround' in oc:
|
||||
vlan_bug_workaround = oc['vlan-bug-workaround'] == 'true'
|
||||
elif driver in NO_VLAN_WORKAROUND_DRIVERS:
|
||||
vlan_bug_workaround = False
|
||||
else:
|
||||
vlan_bug_workaround = netdev_has_vlan_accel(dev)
|
||||
|
||||
datapath_modify_config(self._vsctl_argv)
|
||||
if self._bridge_flows:
|
||||
ofports = []
|
||||
physical_devices = datapath_get_physical_pifs(self._dp)
|
||||
if len(physical_devices) > 1:
|
||||
for slave in physical_devices:
|
||||
name = pif_netdev_name(slave)
|
||||
ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport'])
|
||||
ofports.append(ofport)
|
||||
else:
|
||||
name = pif_netdev_name(self._dp)
|
||||
ofport = vswitchCfgQuery(['get', 'interface', name, 'ofport'])
|
||||
ofports.append(ofport)
|
||||
dpname = pif_bridge_name(self._dp)
|
||||
for flow in self._bridge_flows:
|
||||
if flow.find('in_port=%s') != -1 or flow.find('actions=%s') != -1:
|
||||
for port in ofports:
|
||||
f = flow % (port.decode())
|
||||
run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, f])
|
||||
else:
|
||||
run_command(['/usr/bin/ovs-ofctl', 'add-flow', dpname, flow])
|
||||
|
||||
def post(self):
|
||||
for p in self._extra_ports:
|
||||
log("action_up: bring up %s" % p)
|
||||
netdev_up(p)
|
||||
|
||||
def bring_down(self):
|
||||
vsctl_argv = []
|
||||
|
||||
dp = self._dp
|
||||
ipdev = self._ipdev
|
||||
|
||||
bridge = pif_bridge_name(dp)
|
||||
|
||||
log("deconfigure ipdev %s on %s" % (ipdev,bridge))
|
||||
vsctl_argv += ["# deconfigure ipdev %s" % ipdev]
|
||||
vsctl_argv += datapath_deconfigure_ipdev(ipdev)
|
||||
|
||||
if pif_is_vlan(self._pif):
|
||||
# Delete the VLAN bridge.
|
||||
vsctl_argv += deconfigure_bridge(self._pif)
|
||||
|
||||
# If the VLAN's slave is attached, leave datapath setup.
|
||||
slave = pif_get_vlan_slave(self._pif)
|
||||
if db().get_pif_record(slave)['currently_attached']:
|
||||
log("action_down: vlan slave is currently attached")
|
||||
dp = None
|
||||
|
||||
# If the VLAN's slave has other VLANs that are attached, leave datapath setup.
|
||||
for master in pif_get_vlan_masters(slave):
|
||||
if master != self._pif and db().get_pif_record(master)['currently_attached']:
|
||||
log("action_down: vlan slave has other master: %s" % pif_netdev_name(master))
|
||||
dp = None
|
||||
|
||||
# Otherwise, take down the datapath too (fall through)
|
||||
if dp:
|
||||
log("action_down: no more masters, bring down slave %s" % bridge)
|
||||
else:
|
||||
# Stop here if this PIF has attached VLAN masters.
|
||||
masters = [db().get_pif_record(m)['VLAN'] for m in pif_get_vlan_masters(self._pif) if db().get_pif_record(m)['currently_attached']]
|
||||
if len(masters) > 0:
|
||||
log("Leaving datapath %s up due to currently attached VLAN masters %s" % (bridge, masters))
|
||||
dp = None
|
||||
|
||||
if dp:
|
||||
vsctl_argv += deconfigure_bridge(dp)
|
||||
|
||||
physical_devices = [pif_netdev_name(p) for p in datapath_get_physical_pifs(dp)]
|
||||
|
||||
log("action_down: bring down physical devices - %s" % physical_devices)
|
||||
|
||||
for p in physical_devices:
|
||||
netdev_down(p)
|
||||
|
||||
datapath_modify_config(vsctl_argv)
|
||||
|
||||
#
|
||||
# utility methods
|
||||
#
|
||||
|
||||
def vswitchCfgQuery(action_args):
|
||||
cmd = ['%s/usr/bin/ovs-vsctl' % root_prefix(),
|
||||
'-vconsole:off'] + action_args
|
||||
output = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()
|
||||
if len(output) == 0 or output[0] == None:
|
||||
output = ""
|
||||
else:
|
||||
output = output[0].strip()
|
||||
return output
|
@@ -1,739 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2008,2009 Citrix Systems, Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Lesser General Public License as published
|
||||
# by the Free Software Foundation; version 2.1 only. with the special
|
||||
# exception on linking described in file LICENSE.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Lesser General Public License for more details.
|
||||
#
|
||||
"""Usage:
|
||||
|
||||
%(command-name)s <PIF> up
|
||||
%(command-name)s <PIF> down
|
||||
%(command-name)s rewrite
|
||||
%(command-name)s --force <BRIDGE> up
|
||||
%(command-name)s --force <BRIDGE> down
|
||||
%(command-name)s --force <BRIDGE> rewrite --device=<INTERFACE> --mac=<MAC-ADDRESS> <CONFIG>
|
||||
|
||||
where <PIF> is one of:
|
||||
--session <SESSION-REF> --pif <PIF-REF>
|
||||
--pif-uuid <PIF-UUID>
|
||||
and <CONFIG> is one of:
|
||||
--mode=dhcp
|
||||
--mode=static --ip=<IPADDR> --netmask=<NM> [--gateway=<GW>]
|
||||
|
||||
Options:
|
||||
--session A session reference to use to access the xapi DB
|
||||
--pif A PIF reference within the session.
|
||||
--pif-uuid The UUID of a PIF.
|
||||
--force An interface name.
|
||||
--root-prefix=DIR Use DIR as alternate root directory (for testing).
|
||||
--no-syslog Write log messages to stderr instead of system log.
|
||||
"""
|
||||
|
||||
# Notes:
|
||||
# 1. Every pif belongs to exactly one network
|
||||
# 2. Every network has zero or one pifs
|
||||
# 3. A network may have an associated bridge, allowing vifs to be attached
|
||||
# 4. A network may be bridgeless (there's no point having a bridge over a storage pif)
|
||||
|
||||
from InterfaceReconfigure import *
|
||||
|
||||
import os, sys, getopt
|
||||
import syslog
|
||||
import traceback
|
||||
import re
|
||||
import random
|
||||
import syslog
|
||||
|
||||
management_pif = None
|
||||
|
||||
dbcache_file = "/var/xapi/network.dbcache"
|
||||
|
||||
#
|
||||
# Logging.
|
||||
#
|
||||
|
||||
def log_pif_action(action, pif):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
rec = {}
|
||||
rec['uuid'] = pifrec['uuid']
|
||||
rec['ip_configuration_mode'] = pifrec['ip_configuration_mode']
|
||||
rec['action'] = action
|
||||
rec['pif_netdev_name'] = pif_netdev_name(pif)
|
||||
rec['message'] = "Bring %(action)s PIF %(uuid)s" % rec
|
||||
log("%(message)s: %(pif_netdev_name)s configured as %(ip_configuration_mode)s" % rec)
|
||||
|
||||
#
|
||||
# Exceptions.
|
||||
#
|
||||
|
||||
class Usage(Exception):
|
||||
def __init__(self, msg):
|
||||
Exception.__init__(self)
|
||||
self.msg = msg
|
||||
|
||||
#
|
||||
# Boot from Network filesystem or device.
|
||||
#
|
||||
|
||||
def check_allowed(pif):
|
||||
"""Determine whether interface-reconfigure should be manipulating this PIF.
|
||||
|
||||
Used to prevent system PIFs (such as network root disk) from being interfered with.
|
||||
"""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
try:
|
||||
f = open(root_prefix() + "/proc/ardence")
|
||||
macline = filter(lambda x: x.startswith("HWaddr:"), f.readlines())
|
||||
f.close()
|
||||
if len(macline) == 1:
|
||||
p = re.compile(".*\s%(MAC)s\s.*" % pifrec, re.IGNORECASE)
|
||||
if p.match(macline[0]):
|
||||
log("Skipping PVS device %(device)s (%(MAC)s)" % pifrec)
|
||||
return False
|
||||
except IOError:
|
||||
pass
|
||||
return True
|
||||
|
||||
#
|
||||
# Bare Network Devices -- network devices without IP configuration
|
||||
#
|
||||
|
||||
def netdev_remap_name(pif, already_renamed=[]):
|
||||
"""Check whether 'pif' exists and has the correct MAC.
|
||||
If not, try to find a device with the correct MAC and rename it.
|
||||
'already_renamed' is used to avoid infinite recursion.
|
||||
"""
|
||||
|
||||
def read1(name):
|
||||
file = None
|
||||
try:
|
||||
file = open(name, 'r')
|
||||
return file.readline().rstrip('\n')
|
||||
finally:
|
||||
if file != None:
|
||||
file.close()
|
||||
|
||||
def get_netdev_mac(device):
|
||||
try:
|
||||
return read1("%s/sys/class/net/%s/address" % (root_prefix(), device))
|
||||
except:
|
||||
# Probably no such device.
|
||||
return None
|
||||
|
||||
def get_netdev_tx_queue_len(device):
|
||||
try:
|
||||
return int(read1("%s/sys/class/net/%s/tx_queue_len" % (root_prefix(), device)))
|
||||
except:
|
||||
# Probably no such device.
|
||||
return None
|
||||
|
||||
def get_netdev_by_mac(mac):
|
||||
for device in os.listdir(root_prefix() + "/sys/class/net"):
|
||||
dev_mac = get_netdev_mac(device)
|
||||
if (dev_mac and mac.lower() == dev_mac.lower() and
|
||||
get_netdev_tx_queue_len(device)):
|
||||
return device
|
||||
return None
|
||||
|
||||
def rename_netdev(old_name, new_name):
|
||||
raise Error("Trying to rename %s to %s - This functionality has been removed" % (old_name, new_name))
|
||||
# log("Changing the name of %s to %s" % (old_name, new_name))
|
||||
# run_command(['/sbin/ip', 'link', 'set', old_name, 'down'])
|
||||
# if not run_command(['/sbin/ip', 'link', 'set', old_name, 'name', new_name]):
|
||||
# raise Error("Could not rename %s to %s" % (old_name, new_name))
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
device = pifrec['device']
|
||||
mac = pifrec['MAC']
|
||||
|
||||
# Is there a network device named 'device' at all?
|
||||
device_exists = netdev_exists(device)
|
||||
if device_exists:
|
||||
# Yes. Does it have MAC 'mac'?
|
||||
found_mac = get_netdev_mac(device)
|
||||
if found_mac and mac.lower() == found_mac.lower():
|
||||
# Yes, everything checks out the way we want. Nothing to do.
|
||||
return
|
||||
else:
|
||||
log("No network device %s" % device)
|
||||
|
||||
# What device has MAC 'mac'?
|
||||
cur_device = get_netdev_by_mac(mac)
|
||||
if not cur_device:
|
||||
log("No network device has MAC %s" % mac)
|
||||
return
|
||||
|
||||
# First rename 'device', if it exists, to get it out of the way
|
||||
# for 'cur_device' to replace it.
|
||||
if device_exists:
|
||||
rename_netdev(device, "dev%d" % random.getrandbits(24))
|
||||
|
||||
# Rename 'cur_device' to 'device'.
|
||||
rename_netdev(cur_device, device)
|
||||
|
||||
#
|
||||
# IP Network Devices -- network devices with IP configuration
|
||||
#
|
||||
|
||||
def ifdown(netdev):
|
||||
"""Bring down a network interface"""
|
||||
if not netdev_exists(netdev):
|
||||
log("ifdown: device %s does not exist, ignoring" % netdev)
|
||||
return
|
||||
if not os.path.exists("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), netdev)):
|
||||
log("ifdown: device %s exists but ifcfg-%s does not" % (netdev,netdev))
|
||||
run_command(["/sbin/ip", "link", "set", netdev, 'down'])
|
||||
return
|
||||
run_command(["/sbin/ifdown", netdev])
|
||||
|
||||
def ifup(netdev):
|
||||
"""Bring up a network interface"""
|
||||
if not os.path.exists(root_prefix() + "/etc/sysconfig/network-scripts/ifcfg-%s" % netdev):
|
||||
raise Error("ifup: device %s exists but ifcfg-%s does not" % (netdev,netdev))
|
||||
d = os.getenv("DHCLIENTARGS","")
|
||||
if os.path.exists("/etc/firstboot.d/data/firstboot_in_progress"):
|
||||
os.putenv("DHCLIENTARGS", d + " -T 240 " )
|
||||
run_command(["/sbin/ifup", netdev])
|
||||
os.putenv("DHCLIENTARGS", d )
|
||||
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
def pif_rename_physical_devices(pif):
|
||||
if pif_is_tunnel(pif):
|
||||
return
|
||||
|
||||
if pif_is_vlan(pif):
|
||||
pif = pif_get_vlan_slave(pif)
|
||||
|
||||
if pif_is_bond(pif):
|
||||
pifs = pif_get_bond_slaves(pif)
|
||||
else:
|
||||
pifs = [pif]
|
||||
|
||||
for pif in pifs:
|
||||
netdev_remap_name(pif)
|
||||
|
||||
#
|
||||
# IP device configuration
|
||||
#
|
||||
|
||||
def ipdev_configure_static_routes(interface, oc, f):
|
||||
"""Open a route-<interface> file for static routes.
|
||||
|
||||
Opens the static routes configuration file for interface and writes one
|
||||
line for each route specified in the network's other config "static-routes" value.
|
||||
E.g. if
|
||||
interface ( RO): xenbr1
|
||||
other-config (MRW): static-routes: 172.16.0.0/15/192.168.0.3,172.18.0.0/16/192.168.0.4;...
|
||||
|
||||
Then route-xenbr1 should be
|
||||
172.16.0.0/15 via 192.168.0.3 dev xenbr1
|
||||
172.18.0.0/16 via 192.168.0.4 dev xenbr1
|
||||
"""
|
||||
if 'static-routes' in oc:
|
||||
# The key is present - extract comma separates entries
|
||||
lines = oc['static-routes'].split(',')
|
||||
else:
|
||||
# The key is not present, i.e. there are no static routes
|
||||
lines = []
|
||||
|
||||
child = ConfigurationFile("%s/etc/sysconfig/network-scripts/route-%s" % (root_prefix(), interface))
|
||||
child.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
|
||||
(os.path.basename(child.path()), os.path.basename(sys.argv[0])))
|
||||
|
||||
try:
|
||||
for l in lines:
|
||||
network, masklen, gateway = l.split('/')
|
||||
child.write("%s/%s via %s dev %s\n" % (network, masklen, gateway, interface))
|
||||
|
||||
f.attach_child(child)
|
||||
child.close()
|
||||
|
||||
except ValueError as e:
|
||||
log("Error in other-config['static-routes'] format for network %s: %s" % (interface, e))
|
||||
|
||||
def ipdev_open_ifcfg(pif):
|
||||
ipdev = pif_ipdev_name(pif)
|
||||
|
||||
log("Writing network configuration for %s" % ipdev)
|
||||
|
||||
f = ConfigurationFile("%s/etc/sysconfig/network-scripts/ifcfg-%s" % (root_prefix(), ipdev))
|
||||
|
||||
f.write("# DO NOT EDIT: This file (%s) was autogenerated by %s\n" % \
|
||||
(os.path.basename(f.path()), os.path.basename(sys.argv[0])))
|
||||
f.write("XEMANAGED=yes\n")
|
||||
f.write("DEVICE=%s\n" % ipdev)
|
||||
f.write("ONBOOT=no\n")
|
||||
f.write("NOZEROCONF=yes\n")
|
||||
|
||||
return f
|
||||
|
||||
def ipdev_configure_network(pif, dp):
|
||||
"""Write the configuration file for a network.
|
||||
|
||||
Writes configuration derived from the network object into the relevant
|
||||
ifcfg file. The configuration file is passed in, but if the network is
|
||||
bridgeless it will be ifcfg-<interface>, otherwise it will be ifcfg-<bridge>.
|
||||
|
||||
This routine may also write ifcfg files of the networks corresponding to other PIFs
|
||||
in order to maintain consistency.
|
||||
|
||||
params:
|
||||
pif: Opaque_ref of pif
|
||||
dp: Datapath object
|
||||
"""
|
||||
|
||||
pifrec = db().get_pif_record(pif)
|
||||
nw = pifrec['network']
|
||||
nwrec = db().get_network_record(nw)
|
||||
|
||||
ipdev = pif_ipdev_name(pif)
|
||||
|
||||
f = ipdev_open_ifcfg(pif)
|
||||
|
||||
mode = pifrec['ip_configuration_mode']
|
||||
log("Configuring %s using %s configuration" % (ipdev, mode))
|
||||
|
||||
oc = None
|
||||
if 'other_config' in pifrec:
|
||||
oc = pifrec['other_config']
|
||||
|
||||
dp.configure_ipdev(f)
|
||||
|
||||
if pifrec['ip_configuration_mode'] == "DHCP":
|
||||
f.write("BOOTPROTO=dhcp\n")
|
||||
f.write("PERSISTENT_DHCLIENT=yes\n")
|
||||
elif pifrec['ip_configuration_mode'] == "Static":
|
||||
f.write("BOOTPROTO=none\n")
|
||||
f.write("NETMASK=%(netmask)s\n" % pifrec)
|
||||
f.write("IPADDR=%(IP)s\n" % pifrec)
|
||||
f.write("GATEWAY=%(gateway)s\n" % pifrec)
|
||||
elif pifrec['ip_configuration_mode'] == "None":
|
||||
f.write("BOOTPROTO=none\n")
|
||||
else:
|
||||
raise Error("Unknown ip-configuration-mode %s" % pifrec['ip_configuration_mode'])
|
||||
|
||||
if 'other_config' in nwrec:
|
||||
settings,offload = ethtool_settings(nwrec['other_config'])
|
||||
if len(settings):
|
||||
f.write("ETHTOOL_OPTS=\"%s\"\n" % str.join(" ", settings))
|
||||
if len(offload):
|
||||
f.write("ETHTOOL_OFFLOAD_OPTS=\"%s\"\n" % str.join(" ", offload))
|
||||
|
||||
ipdev_configure_static_routes(ipdev, nwrec['other_config'], f)
|
||||
|
||||
mtu = mtu_setting(nw, "Network", nwrec['other_config'])
|
||||
if mtu:
|
||||
f.write("MTU=%s\n" % mtu)
|
||||
|
||||
|
||||
if 'DNS' in pifrec and pifrec['DNS'] != "":
|
||||
ServerList = pifrec['DNS'].split(",")
|
||||
for i in range(len(ServerList)): f.write("DNS%d=%s\n" % (i+1, ServerList[i]))
|
||||
if oc and 'domain' in oc:
|
||||
f.write("DOMAIN='%s'\n" % oc['domain'].replace(',', ' '))
|
||||
|
||||
# There can be only one DNSDEV and one GATEWAYDEV in /etc/sysconfig/network.
|
||||
#
|
||||
# The peerdns pif will be the one with
|
||||
# pif::other-config:peerdns=true, or the mgmt pif if none have
|
||||
# this set.
|
||||
#
|
||||
# The gateway pif will be the one with
|
||||
# pif::other-config:defaultroute=true, or the mgmt pif if none
|
||||
# have this set.
|
||||
|
||||
# Work out which pif on this host should be the DNSDEV and which
|
||||
# should be the GATEWAYDEV
|
||||
#
|
||||
# Note: we prune out the bond master pif (if it exists). This is
|
||||
# because when we are called to bring up an interface with a bond
|
||||
# master, it is implicit that we should bring down that master.
|
||||
|
||||
pifs_on_host = [p for p in db().get_all_pifs() if not p in pif_get_bond_masters(pif)]
|
||||
|
||||
# now prune out bond slaves as they are not connected to the IP
|
||||
# stack and so cannot be used as gateway or DNS devices.
|
||||
pifs_on_host = [ p for p in pifs_on_host if len(pif_get_bond_masters(p)) == 0]
|
||||
|
||||
# loop through all the pifs on this host looking for one with
|
||||
# other-config:peerdns = true, and one with
|
||||
# other-config:default-route=true
|
||||
peerdns_pif = None
|
||||
defaultroute_pif = None
|
||||
for __pif in pifs_on_host:
|
||||
__pifrec = db().get_pif_record(__pif)
|
||||
__oc = __pifrec['other_config']
|
||||
if 'peerdns' in __oc and __oc['peerdns'] == 'true':
|
||||
if peerdns_pif == None:
|
||||
peerdns_pif = __pif
|
||||
else:
|
||||
log('Warning: multiple pifs with "peerdns=true" - choosing %s and ignoring %s' % \
|
||||
(db().get_pif_record(peerdns_pif)['device'], __pifrec['device']))
|
||||
if 'defaultroute' in __oc and __oc['defaultroute'] == 'true':
|
||||
if defaultroute_pif == None:
|
||||
defaultroute_pif = __pif
|
||||
else:
|
||||
log('Warning: multiple pifs with "defaultroute=true" - choosing %s and ignoring %s' % \
|
||||
(db().get_pif_record(defaultroute_pif)['device'], __pifrec['device']))
|
||||
|
||||
# If no pif is explicitly specified then use the mgmt pif for
|
||||
# peerdns/defaultroute.
|
||||
if peerdns_pif == None:
|
||||
peerdns_pif = management_pif
|
||||
if defaultroute_pif == None:
|
||||
defaultroute_pif = management_pif
|
||||
|
||||
is_dnsdev = peerdns_pif == pif
|
||||
is_gatewaydev = defaultroute_pif == pif
|
||||
|
||||
if is_dnsdev or is_gatewaydev:
|
||||
fnetwork = ConfigurationFile(root_prefix() + "/etc/sysconfig/network")
|
||||
for line in fnetwork.readlines():
|
||||
if is_dnsdev and line.lstrip().startswith('DNSDEV='):
|
||||
fnetwork.write('DNSDEV=%s\n' % ipdev)
|
||||
is_dnsdev = False
|
||||
elif is_gatewaydev and line.lstrip().startswith('GATEWAYDEV='):
|
||||
fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
|
||||
is_gatewaydev = False
|
||||
else:
|
||||
fnetwork.write(line)
|
||||
|
||||
if is_dnsdev:
|
||||
fnetwork.write('DNSDEV=%s\n' % ipdev)
|
||||
if is_gatewaydev:
|
||||
fnetwork.write('GATEWAYDEV=%s\n' % ipdev)
|
||||
|
||||
fnetwork.close()
|
||||
f.attach_child(fnetwork)
|
||||
|
||||
return f
|
||||
|
||||
#
|
||||
# Toplevel actions
|
||||
#
|
||||
|
||||
def action_up(pif, force):
|
||||
pifrec = db().get_pif_record(pif)
|
||||
|
||||
ipdev = pif_ipdev_name(pif)
|
||||
dp = DatapathFactory()(pif)
|
||||
|
||||
log("action_up: %s" % ipdev)
|
||||
|
||||
f = ipdev_configure_network(pif, dp)
|
||||
|
||||
dp.preconfigure(f)
|
||||
|
||||
f.close()
|
||||
|
||||
pif_rename_physical_devices(pif)
|
||||
|
||||
# if we are not forcing the interface up then attempt to tear down
|
||||
# any existing devices which might interfere with brinign this one
|
||||
# up.
|
||||
if not force:
|
||||
ifdown(ipdev)
|
||||
|
||||
dp.bring_down_existing()
|
||||
|
||||
try:
|
||||
f.apply()
|
||||
|
||||
dp.configure()
|
||||
|
||||
ifup(ipdev)
|
||||
|
||||
dp.post()
|
||||
|
||||
# Update /etc/issue (which contains the IP address of the management interface)
|
||||
os.system(root_prefix() + "/sbin/update-issue")
|
||||
|
||||
f.commit()
|
||||
except Error as e:
|
||||
log("failed to apply changes: %s" % e.msg)
|
||||
f.revert()
|
||||
raise
|
||||
|
||||
def action_down(pif):
|
||||
ipdev = pif_ipdev_name(pif)
|
||||
dp = DatapathFactory()(pif)
|
||||
|
||||
log("action_down: %s" % ipdev)
|
||||
|
||||
ifdown(ipdev)
|
||||
|
||||
dp.bring_down()
|
||||
|
||||
def action_rewrite():
|
||||
DatapathFactory().rewrite()
|
||||
|
||||
# This is useful for reconfiguring the mgmt interface after having lost connectivity to the pool master
|
||||
def action_force_rewrite(bridge, config):
|
||||
def getUUID():
|
||||
import subprocess
|
||||
uuid,_ = subprocess.Popen(['uuidgen'], stdout = subprocess.PIPE).communicate()
|
||||
return uuid.strip()
|
||||
|
||||
# Notes:
|
||||
# 1. that this assumes the interface is bridged
|
||||
# 2. If --gateway is given it will make that the default gateway for the host
|
||||
|
||||
# extract the configuration
|
||||
try:
|
||||
mode = config['mode']
|
||||
mac = config['mac']
|
||||
interface = config['device']
|
||||
except:
|
||||
raise Usage("Please supply --mode, --mac and --device")
|
||||
|
||||
if mode == 'static':
|
||||
try:
|
||||
netmask = config['netmask']
|
||||
ip = config['ip']
|
||||
except:
|
||||
raise Usage("Please supply --netmask and --ip")
|
||||
try:
|
||||
gateway = config['gateway']
|
||||
except:
|
||||
gateway = None
|
||||
elif mode != 'dhcp':
|
||||
raise Usage("--mode must be either static or dhcp")
|
||||
|
||||
if 'vlan' in config:
|
||||
is_vlan = True
|
||||
vlan_slave, vlan_vid = config['vlan'].split('.')
|
||||
else:
|
||||
is_vlan = False
|
||||
|
||||
if is_vlan:
|
||||
raise Error("Force rewrite of VLAN not implemented")
|
||||
|
||||
log("Configuring %s using %s configuration" % (bridge, mode))
|
||||
|
||||
f = ConfigurationFile(root_prefix() + dbcache_file)
|
||||
|
||||
pif_uuid = getUUID()
|
||||
network_uuid = getUUID()
|
||||
|
||||
f.write('<?xml version="1.0" ?>\n')
|
||||
f.write('<xenserver-network-configuration>\n')
|
||||
f.write('\t<pif ref="OpaqueRef:%s">\n' % pif_uuid)
|
||||
f.write('\t\t<network>OpaqueRef:%s</network>\n' % network_uuid)
|
||||
f.write('\t\t<management>True</management>\n')
|
||||
f.write('\t\t<uuid>%sPif</uuid>\n' % interface)
|
||||
f.write('\t\t<bond_slave_of>OpaqueRef:NULL</bond_slave_of>\n')
|
||||
f.write('\t\t<bond_master_of/>\n')
|
||||
f.write('\t\t<VLAN_slave_of/>\n')
|
||||
f.write('\t\t<VLAN_master_of>OpaqueRef:NULL</VLAN_master_of>\n')
|
||||
f.write('\t\t<VLAN>-1</VLAN>\n')
|
||||
f.write('\t\t<tunnel_access_PIF_of/>\n')
|
||||
f.write('\t\t<tunnel_transport_PIF_of/>\n')
|
||||
f.write('\t\t<device>%s</device>\n' % interface)
|
||||
f.write('\t\t<MAC>%s</MAC>\n' % mac)
|
||||
f.write('\t\t<other_config/>\n')
|
||||
if mode == 'dhcp':
|
||||
f.write('\t\t<ip_configuration_mode>DHCP</ip_configuration_mode>\n')
|
||||
f.write('\t\t<IP></IP>\n')
|
||||
f.write('\t\t<netmask></netmask>\n')
|
||||
f.write('\t\t<gateway></gateway>\n')
|
||||
f.write('\t\t<DNS></DNS>\n')
|
||||
elif mode == 'static':
|
||||
f.write('\t\t<ip_configuration_mode>Static</ip_configuration_mode>\n')
|
||||
f.write('\t\t<IP>%s</IP>\n' % ip)
|
||||
f.write('\t\t<netmask>%s</netmask>\n' % netmask)
|
||||
if gateway is not None:
|
||||
f.write('\t\t<gateway>%s</gateway>\n' % gateway)
|
||||
f.write('\t\t<DNS></DNS>\n')
|
||||
else:
|
||||
raise Error("Unknown mode %s" % mode)
|
||||
f.write('\t</pif>\n')
|
||||
|
||||
f.write('\t<network ref="OpaqueRef:%s">\n' % network_uuid)
|
||||
f.write('\t\t<uuid>InitialManagementNetwork</uuid>\n')
|
||||
f.write('\t\t<PIFs>\n')
|
||||
f.write('\t\t\t<PIF>OpaqueRef:%s</PIF>\n' % pif_uuid)
|
||||
f.write('\t\t</PIFs>\n')
|
||||
f.write('\t\t<bridge>%s</bridge>\n' % bridge)
|
||||
f.write('\t\t<other_config/>\n')
|
||||
f.write('\t</network>\n')
|
||||
f.write('</xenserver-network-configuration>\n')
|
||||
|
||||
f.close()
|
||||
|
||||
try:
|
||||
f.apply()
|
||||
f.commit()
|
||||
except Error as e:
|
||||
log("failed to apply changes: %s" % e.msg)
|
||||
f.revert()
|
||||
raise
|
||||
|
||||
def main(argv=None):
|
||||
global management_pif
|
||||
|
||||
session = None
|
||||
pif_uuid = None
|
||||
pif = None
|
||||
|
||||
force_interface = None
|
||||
force_management = False
|
||||
|
||||
if argv is None:
|
||||
argv = sys.argv
|
||||
|
||||
try:
|
||||
try:
|
||||
shortops = "h"
|
||||
longops = [ "pif=", "pif-uuid=",
|
||||
"session=",
|
||||
"force=",
|
||||
"force-interface=",
|
||||
"management",
|
||||
"mac=", "device=", "mode=", "ip=", "netmask=", "gateway=",
|
||||
"root-prefix=",
|
||||
"no-syslog",
|
||||
"help" ]
|
||||
arglist, args = getopt.gnu_getopt(argv[1:], shortops, longops)
|
||||
except getopt.GetoptError as msg:
|
||||
raise Usage(msg)
|
||||
|
||||
force_rewrite_config = {}
|
||||
|
||||
for o,a in arglist:
|
||||
if o == "--pif":
|
||||
pif = a
|
||||
elif o == "--pif-uuid":
|
||||
pif_uuid = a
|
||||
elif o == "--session":
|
||||
session = a
|
||||
elif o == "--force-interface" or o == "--force":
|
||||
force_interface = a
|
||||
elif o == "--management":
|
||||
force_management = True
|
||||
elif o in ["--mac", "--device", "--mode", "--ip", "--netmask", "--gateway"]:
|
||||
force_rewrite_config[o[2:]] = a
|
||||
elif o == "--root-prefix":
|
||||
set_root_prefix(a)
|
||||
elif o == "--no-syslog":
|
||||
set_log_destination("stderr")
|
||||
elif o == "-h" or o == "--help":
|
||||
print(__doc__ % {'command-name': os.path.basename(argv[0])})
|
||||
return 0
|
||||
|
||||
if get_log_destination() == "syslog":
|
||||
syslog.openlog(os.path.basename(argv[0]))
|
||||
log("Called as " + str.join(" ", argv))
|
||||
|
||||
if len(args) < 1:
|
||||
raise Usage("Required option <action> not present")
|
||||
if len(args) > 1:
|
||||
raise Usage("Too many arguments")
|
||||
|
||||
action = args[0]
|
||||
|
||||
if not action in ["up", "down", "rewrite", "rewrite-configuration"]:
|
||||
raise Usage("Unknown action \"%s\"" % action)
|
||||
|
||||
# backwards compatibility
|
||||
if action == "rewrite-configuration": action = "rewrite"
|
||||
|
||||
if ( session or pif ) and pif_uuid:
|
||||
raise Usage("--session/--pif and --pif-uuid are mutually exclusive.")
|
||||
if ( session and not pif ) or ( not session and pif ):
|
||||
raise Usage("--session and --pif must be used together.")
|
||||
if force_interface and ( session or pif or pif_uuid ):
|
||||
raise Usage("--force is mutually exclusive with --session, --pif and --pif-uuid")
|
||||
if len(force_rewrite_config) and not (force_interface and action == "rewrite"):
|
||||
raise Usage("\"--force rewrite\" needed for --device, --mode, --ip, --netmask, and --gateway")
|
||||
if (action == "rewrite") and (pif or pif_uuid ):
|
||||
raise Usage("rewrite action does not take --pif or --pif-uuid")
|
||||
|
||||
global db
|
||||
if force_interface:
|
||||
log("Force interface %s %s" % (force_interface, action))
|
||||
|
||||
if action == "rewrite":
|
||||
action_force_rewrite(force_interface, force_rewrite_config)
|
||||
elif action in ["up", "down"]:
|
||||
db_init_from_cache(dbcache_file)
|
||||
pif = db().get_pif_by_bridge(force_interface)
|
||||
management_pif = db().get_management_pif()
|
||||
|
||||
if action == "up":
|
||||
action_up(pif, True)
|
||||
elif action == "down":
|
||||
action_down(pif)
|
||||
else:
|
||||
raise Error("Unknown action %s" % action)
|
||||
else:
|
||||
db_init_from_xenapi(session)
|
||||
|
||||
if pif_uuid:
|
||||
pif = db().get_pif_by_uuid(pif_uuid)
|
||||
|
||||
if action == "rewrite":
|
||||
action_rewrite()
|
||||
else:
|
||||
if not pif:
|
||||
raise Usage("No PIF given")
|
||||
|
||||
if force_management:
|
||||
# pif is going to be the management pif
|
||||
management_pif = pif
|
||||
else:
|
||||
# pif is not going to be the management pif.
|
||||
# Search DB cache for pif on same host with management=true
|
||||
pifrec = db().get_pif_record(pif)
|
||||
management_pif = db().get_management_pif()
|
||||
|
||||
log_pif_action(action, pif)
|
||||
|
||||
if not check_allowed(pif):
|
||||
return 0
|
||||
|
||||
if action == "up":
|
||||
action_up(pif, False)
|
||||
elif action == "down":
|
||||
action_down(pif)
|
||||
else:
|
||||
raise Error("Unknown action %s" % action)
|
||||
|
||||
# Save cache.
|
||||
db().save(dbcache_file)
|
||||
|
||||
except Usage as err:
|
||||
sys.stderr.write(err.msg + "\n")
|
||||
sys.stderr.write("For help use --help.\n")
|
||||
sys.stderr.flush()
|
||||
return 2
|
||||
except Error as err:
|
||||
log(err.msg)
|
||||
return 1
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
rc = 1
|
||||
try:
|
||||
rc = main()
|
||||
except:
|
||||
ex = sys.exc_info()
|
||||
err = traceback.format_exception(*ex)
|
||||
for exline in err:
|
||||
log(exline)
|
||||
|
||||
syslog.closelog()
|
||||
|
||||
sys.exit(rc)
|
@@ -1,331 +0,0 @@
|
||||
# Copyright (c) 2009,2010,2011,2012,2013 Nicira, Inc.
|
||||
# Copyright (c) 2007-2011 Citrix Systems Inc.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; version 2 only.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License along
|
||||
# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
from XSConsoleLog import *
|
||||
|
||||
import os
|
||||
import socket
|
||||
import subprocess
|
||||
|
||||
vsctl="/usr/bin/ovs-vsctl"
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise Exception("This script is a plugin for xsconsole and cannot run independently")
|
||||
|
||||
from XSConsoleStandard import *
|
||||
|
||||
class VSwitchService:
|
||||
service = {}
|
||||
|
||||
def __init__(self, name, processname=None):
|
||||
self.name = name
|
||||
self.processname = processname
|
||||
if self.processname == None:
|
||||
self.processname = name
|
||||
|
||||
def version(self):
|
||||
try:
|
||||
output = ShellPipe(["service", self.name, "version"]).Stdout()
|
||||
except StandardError as e:
|
||||
XSLogError("vswitch version retrieval error: " + str(e))
|
||||
return "<unknown>"
|
||||
for line in output:
|
||||
if self.processname in line:
|
||||
return line.split()[-1]
|
||||
return "<unknown>"
|
||||
|
||||
def status(self):
|
||||
try:
|
||||
output = ShellPipe(["service", self.name, "status"]).Stdout()
|
||||
except StandardError as e:
|
||||
XSLogError("vswitch status retrieval error: " + str(e))
|
||||
return "<unknown>"
|
||||
if len(output) == 0:
|
||||
return "<unknown>"
|
||||
for line in output:
|
||||
if self.processname not in line:
|
||||
continue
|
||||
elif "running" in line:
|
||||
return "Running"
|
||||
elif "stop" in line:
|
||||
return "Stopped"
|
||||
else:
|
||||
return "<unknown>"
|
||||
return "<unknown>"
|
||||
|
||||
def restart(self):
|
||||
try:
|
||||
ShellPipe(["service", self.name, "restart"]).Call()
|
||||
except StandardError as e:
|
||||
XSLogError("vswitch restart error: " + str(e))
|
||||
|
||||
@classmethod
|
||||
def Inst(cls, name, processname=None):
|
||||
key = name
|
||||
if processname != None:
|
||||
key = key + "-" + processname
|
||||
if name not in cls.service:
|
||||
cls.service[key] = VSwitchService(name, processname)
|
||||
return cls.service[key]
|
||||
|
||||
class VSwitchConfig:
|
||||
|
||||
@staticmethod
|
||||
def Get(action):
|
||||
try:
|
||||
arg = [vsctl, "-vconsole:off"] + action.split()
|
||||
output = ShellPipe(arg).Stdout()
|
||||
except StandardError as e:
|
||||
XSLogError("config retrieval error: " + str(e))
|
||||
return "<unknown>"
|
||||
|
||||
if len(output) == 0:
|
||||
output = ""
|
||||
else:
|
||||
output = output[0].strip()
|
||||
return output
|
||||
|
||||
|
||||
class VSwitchControllerDialogue(Dialogue):
|
||||
def __init__(self):
|
||||
Dialogue.__init__(self)
|
||||
data=Data.Inst()
|
||||
|
||||
self.hostsInPool = 0
|
||||
self.hostsUpdated = 0
|
||||
self.xs_version = data.host.software_version.product_version('')
|
||||
pool = data.GetPoolForThisHost()
|
||||
if pool is not None:
|
||||
self.controller = pool.get("vswitch_controller", "")
|
||||
else:
|
||||
self.controller = ""
|
||||
|
||||
choiceDefs = [
|
||||
ChoiceDef(Lang("Set pool-wide controller"),
|
||||
lambda: self.getController()),
|
||||
ChoiceDef(Lang("Delete pool-wide controller"),
|
||||
lambda: self.deleteController()),
|
||||
ChoiceDef(Lang("Resync server controller config"),
|
||||
lambda: self.syncController()),
|
||||
# ChoiceDef(Lang("Restart ovs-vswitchd"),
|
||||
# lambda: self.restartService("vswitch")),
|
||||
]
|
||||
self.menu = Menu(self, None, Lang("Configure Open vSwitch"), choiceDefs)
|
||||
|
||||
self.ChangeState("INITIAL")
|
||||
|
||||
def BuildPane(self):
|
||||
pane = self.NewPane(DialoguePane(self.parent))
|
||||
pane.TitleSet(Lang("Configure Open vSwitch"))
|
||||
pane.AddBox()
|
||||
|
||||
def ChangeState(self, inState):
|
||||
self.state = inState
|
||||
self.BuildPane()
|
||||
self.UpdateFields()
|
||||
|
||||
def UpdateFields(self):
|
||||
self.Pane().ResetPosition()
|
||||
getattr(self, "UpdateFields" + self.state)() # Dispatch method named 'UpdateFields'+self.state
|
||||
|
||||
def UpdateFieldsINITIAL(self):
|
||||
pane = self.Pane()
|
||||
pane.AddTitleField(Lang("Select an action"))
|
||||
pane.AddMenuField(self.menu)
|
||||
pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Cancel") } )
|
||||
|
||||
def UpdateFieldsGETCONTROLLER(self):
|
||||
pane = self.Pane()
|
||||
pane.ResetFields()
|
||||
|
||||
pane.AddTitleField(Lang("Enter IP address of controller"))
|
||||
pane.AddInputField(Lang("Address", 16), self.controller, "address")
|
||||
pane.AddKeyHelpField( { Lang("<Enter>") : Lang("OK"), Lang("<Esc>") : Lang("Exit") } )
|
||||
if pane.CurrentInput() is None:
|
||||
pane.InputIndexSet(0)
|
||||
|
||||
def HandleKey(self, inKey):
|
||||
handled = False
|
||||
if hasattr(self, "HandleKey" + self.state):
|
||||
handled = getattr(self, "HandleKey" + self.state)(inKey)
|
||||
if not handled and inKey == 'KEY_ESCAPE':
|
||||
Layout.Inst().PopDialogue()
|
||||
handled = True
|
||||
return handled
|
||||
|
||||
def HandleKeyINITIAL(self, inKey):
|
||||
return self.menu.HandleKey(inKey)
|
||||
|
||||
def HandleKeyGETCONTROLLER(self, inKey):
|
||||
pane = self.Pane()
|
||||
if pane.CurrentInput() is None:
|
||||
pane.InputIndexSet(0)
|
||||
if inKey == 'KEY_ENTER':
|
||||
inputValues = pane.GetFieldValues()
|
||||
self.controller = inputValues['address']
|
||||
Layout.Inst().PopDialogue()
|
||||
|
||||
# Make sure the controller is specified as a valid dotted quad
|
||||
try:
|
||||
socket.inet_aton(self.controller)
|
||||
except socket.error:
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Please enter in dotted quad format")))
|
||||
return True
|
||||
|
||||
Layout.Inst().TransientBanner(Lang("Setting controller..."))
|
||||
try:
|
||||
self.SetController(self.controller)
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller successful")))
|
||||
except Exception as e:
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Setting controller failed")))
|
||||
|
||||
self.ChangeState("INITIAL")
|
||||
return True
|
||||
else:
|
||||
return pane.CurrentInput().HandleKey(inKey)
|
||||
|
||||
def restartService(self, name):
|
||||
s = VSwitchService.Inst(name)
|
||||
s.restart()
|
||||
Layout.Inst().PopDialogue()
|
||||
|
||||
def getController(self):
|
||||
self.ChangeState("GETCONTROLLER")
|
||||
self.Pane().InputIndexSet(0)
|
||||
|
||||
def deleteController(self):
|
||||
self.controller = ""
|
||||
Layout.Inst().PopDialogue()
|
||||
Layout.Inst().TransientBanner(Lang("Deleting controller..."))
|
||||
try:
|
||||
self.SetController(None)
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion successful")))
|
||||
except Exception as e:
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Controller deletion failed")))
|
||||
|
||||
def syncController(self):
|
||||
Layout.Inst().PopDialogue()
|
||||
Layout.Inst().TransientBanner(Lang("Resyncing controller setting..."))
|
||||
try:
|
||||
Task.Sync(lambda s: self._updateThisServer(s))
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config successful")))
|
||||
except Exception as e:
|
||||
Layout.Inst().PushDialogue(InfoDialogue(Lang("Resyncing controller config failed")))
|
||||
|
||||
def SetController(self, ip):
|
||||
self.hostsInPool = 0
|
||||
self.hostsUpdated = 0
|
||||
Task.Sync(lambda s: self._modifyPoolConfig(s, ip or ""))
|
||||
# Should be done asynchronously, maybe with an external script?
|
||||
Task.Sync(lambda s: self._updateActiveServers(s))
|
||||
|
||||
def _modifyPoolConfig(self, session, value):
|
||||
"""Modify pool configuration.
|
||||
|
||||
If value == "" then delete configuration, otherwise set to value.
|
||||
"""
|
||||
pools = session.xenapi.pool.get_all()
|
||||
# We assume there is only ever one pool...
|
||||
if len(pools) == 0:
|
||||
XSLogFatal(Lang("No pool found for host."))
|
||||
return
|
||||
if len(pools) > 1:
|
||||
XSLogFatal(Lang("More than one pool for host."))
|
||||
return
|
||||
session.xenapi.pool.set_vswitch_controller(value)
|
||||
Data.Inst().Update()
|
||||
|
||||
def _updateActiveServers(self, session):
|
||||
hosts = session.xenapi.host.get_all()
|
||||
self.hostsUpdated = 0
|
||||
self.hostsInPool = len(hosts)
|
||||
self.UpdateFields()
|
||||
for host in hosts:
|
||||
Layout.Inst().TransientBanner("Updating host %d out of %d"
|
||||
% (self.hostsUpdated + 1, self.hostsInPool))
|
||||
session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
|
||||
self.hostsUpdated = self.hostsUpdated + 1
|
||||
|
||||
def _updateThisServer(self, session):
|
||||
data = Data.Inst()
|
||||
host = data.host.opaqueref()
|
||||
session.xenapi.host.call_plugin(host, "openvswitch-cfg-update", "update", {})
|
||||
|
||||
|
||||
class XSFeatureVSwitch:
|
||||
|
||||
@classmethod
|
||||
def StatusUpdateHandler(cls, inPane):
|
||||
data = Data.Inst()
|
||||
xs_version = data.host.software_version.product_version('')
|
||||
|
||||
inPane.AddTitleField(Lang("Open vSwitch"))
|
||||
|
||||
inPane.NewLine()
|
||||
|
||||
inPane.AddStatusField(Lang("Version", 20),
|
||||
VSwitchService.Inst("openvswitch", "ovs-vswitchd").version())
|
||||
|
||||
inPane.NewLine()
|
||||
|
||||
pool = data.GetPoolForThisHost()
|
||||
if pool is not None:
|
||||
dbController = pool.get("vswitch_controller", "")
|
||||
else:
|
||||
dbController = ""
|
||||
|
||||
if dbController == "":
|
||||
dbController = Lang("<None>")
|
||||
inPane.AddStatusField(Lang("Controller (config)", 20), dbController)
|
||||
controller = VSwitchConfig.Get("get-manager")
|
||||
|
||||
if controller == "":
|
||||
controller = Lang("<None>")
|
||||
elif controller[0:4] == "ssl:":
|
||||
controller = controller.split(':')[1]
|
||||
inPane.AddStatusField(Lang("Controller (in-use)", 20), controller)
|
||||
|
||||
inPane.NewLine()
|
||||
inPane.AddStatusField(Lang("ovs-vswitchd status", 20),
|
||||
VSwitchService.Inst("openvswitch", "ovs-vswitchd").status())
|
||||
inPane.AddStatusField(Lang("ovsdb-server status", 20),
|
||||
VSwitchService.Inst("openvswitch", "ovsdb-server").status())
|
||||
|
||||
inPane.AddKeyHelpField( {
|
||||
Lang("<Enter>") : Lang("Reconfigure"),
|
||||
Lang("<F5>") : Lang("Refresh")
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def ActivateHandler(cls):
|
||||
DialogueUtils.AuthenticatedOnly(lambda: Layout.Inst().PushDialogue(VSwitchControllerDialogue()))
|
||||
|
||||
def Register(self):
|
||||
Importer.RegisterNamedPlugIn(
|
||||
self,
|
||||
'VSwitch', # Key of this plugin for replacement, etc.
|
||||
{
|
||||
'menuname' : 'MENU_NETWORK',
|
||||
'menupriority' : 800,
|
||||
'menutext' : Lang('Open vSwitch') ,
|
||||
'statusupdatehandler' : self.StatusUpdateHandler,
|
||||
'activatehandler' : self.ActivateHandler
|
||||
}
|
||||
)
|
||||
|
||||
# Register this plugin when module is imported, IFF vswitchd is running
|
||||
if os.path.exists('/var/run/openvswitch/ovs-vswitchd.pid'):
|
||||
XSFeatureVSwitch().Register()
|
@@ -1,404 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2009, 2010, 2011, 2012, 2013, 2020 Nicira, 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.
|
||||
|
||||
|
||||
# A daemon to monitor the external_ids columns of the Bridge and
|
||||
# Interface OVSDB tables for changes that require interrogating XAPI.
|
||||
# Its responsibilities include:
|
||||
#
|
||||
# - Set the "bridge-id" key in the Bridge table.
|
||||
# - Set the "iface-id" key in the Interface table.
|
||||
# - Set the fail-mode on internal bridges.
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import XenAPI
|
||||
|
||||
import ovs.daemon
|
||||
import ovs.db.idl
|
||||
import ovs.dirs
|
||||
import ovs.unixctl
|
||||
import ovs.unixctl.server
|
||||
|
||||
vlog = ovs.vlog.Vlog("ovs-xapi-sync")
|
||||
session = None
|
||||
flush_cache = False
|
||||
exiting = False
|
||||
xapi_down = False
|
||||
|
||||
|
||||
def unixctl_exit(conn, unused_argv, unused_aux):
|
||||
global exiting
|
||||
exiting = True
|
||||
conn.reply(None)
|
||||
|
||||
|
||||
def unixctl_flush_cache(conn, unused_argv, unused_aux):
|
||||
global flush_cache
|
||||
flush_cache = True
|
||||
conn.reply(None)
|
||||
|
||||
|
||||
# Set up a session to interact with XAPI.
|
||||
#
|
||||
# On system start-up, OVS comes up before XAPI, so we can't log into the
|
||||
# session until later. Try to do this on-demand, since we won't
|
||||
# actually do anything interesting until XAPI is up.
|
||||
def init_session():
|
||||
global session
|
||||
if session is not None:
|
||||
return True
|
||||
|
||||
try:
|
||||
session = XenAPI.xapi_local()
|
||||
session.xenapi.login_with_password("", "")
|
||||
except XenAPI.Failure as e:
|
||||
session = None
|
||||
vlog.warn("Couldn't login to XAPI (%s)" % e)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_network_by_bridge(br_name):
|
||||
if not init_session():
|
||||
vlog.warn("Failed to get bridge id %s because"
|
||||
" XAPI session could not be initialized" % br_name)
|
||||
return None
|
||||
|
||||
recs = session.xenapi.network.get_all_records_where(
|
||||
'field "bridge"="%s"' % br_name)
|
||||
if len(recs) > 0:
|
||||
return next(iter(recs.values()))
|
||||
|
||||
return None
|
||||
|
||||
|
||||
# There are possibilities when multiple xs-network-uuids are set for a bridge.
|
||||
# In cases like that, we should choose the bridge-id associated with the bridge
|
||||
# name.
|
||||
def get_single_bridge_id(bridge_ids, br_name, default=None):
|
||||
global xapi_down
|
||||
|
||||
rec = get_network_by_bridge(br_name)
|
||||
if rec and rec['uuid'] in bridge_ids:
|
||||
return rec['uuid']
|
||||
|
||||
vlog.warn("Failed to get a single bridge id from Xapi.")
|
||||
xapi_down = True
|
||||
return default
|
||||
|
||||
|
||||
# By default, the "bridge-id" external id in the Bridge table is the
|
||||
# same as "xs-network-uuids". This may be overridden by defining a
|
||||
# "nicira-bridge-id" key in the "other_config" field of the network
|
||||
# record of XAPI. If nicira-bridge-id is undefined returns default.
|
||||
# On error returns None.
|
||||
def get_bridge_id(br_name, default=None):
|
||||
rec = get_network_by_bridge(br_name)
|
||||
if rec:
|
||||
return rec['other_config'].get('nicira-bridge-id', default)
|
||||
return None
|
||||
|
||||
|
||||
# By default, the "iface-id" external id in the Interface table is the
|
||||
# same as "xs-vif-uuid". This may be overridden by defining a
|
||||
# "nicira-iface-id" key in the "other_config" field of the VIF
|
||||
# record of XAPI.
|
||||
def get_iface_id(if_name, xs_vif_uuid):
|
||||
if not if_name.startswith("vif") and not if_name.startswith("tap"):
|
||||
# Treat whatever was passed into 'xs_vif_uuid' as a default
|
||||
# value for non-VIFs.
|
||||
return xs_vif_uuid
|
||||
|
||||
if not init_session():
|
||||
vlog.warn("Failed to get interface id %s because"
|
||||
" XAPI session could not be initialized" % if_name)
|
||||
return xs_vif_uuid
|
||||
|
||||
try:
|
||||
vif = session.xenapi.VIF.get_by_uuid(xs_vif_uuid)
|
||||
rec = session.xenapi.VIF.get_record(vif)
|
||||
return rec['other_config'].get('nicira-iface-id', xs_vif_uuid)
|
||||
except XenAPI.Failure:
|
||||
vlog.warn("Could not find XAPI entry for VIF %s" % if_name)
|
||||
return xs_vif_uuid
|
||||
|
||||
|
||||
# By default, the "vm-id" external id in the Interface table is the
|
||||
# same as "xs-vm-uuid". This may be overridden by defining a
|
||||
# "nicira-vm-id" key in the "other_config" field of the VM
|
||||
# record of XAPI.
|
||||
def get_vm_id(if_name, xs_vm_uuid):
|
||||
if not if_name.startswith("vif") and not if_name.startswith("tap"):
|
||||
# Treat whatever was passed into 'xs_vm_uuid' as a default
|
||||
# value for non-VIFs.
|
||||
return xs_vm_uuid
|
||||
|
||||
if not init_session():
|
||||
vlog.warn("Failed to get vm id for interface id %s because"
|
||||
" XAPI session could not be initialized" % if_name)
|
||||
return xs_vm_uuid
|
||||
|
||||
try:
|
||||
vm = session.xenapi.VM.get_by_uuid(xs_vm_uuid)
|
||||
rec = session.xenapi.VM.get_record(vm)
|
||||
return rec['other_config'].get('nicira-vm-id', xs_vm_uuid)
|
||||
except XenAPI.Failure:
|
||||
vlog.warn("Could not find XAPI entry for VIF %s" % if_name)
|
||||
return xs_vm_uuid
|
||||
|
||||
|
||||
def set_or_delete(d, key, value):
|
||||
if value is None:
|
||||
if key in d:
|
||||
del d[key]
|
||||
return True
|
||||
else:
|
||||
if d.get(key) != value:
|
||||
d[key] = value
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def set_external_id(row, key, value):
|
||||
row.verify("external_ids")
|
||||
external_ids = row.external_ids
|
||||
if set_or_delete(external_ids, key, value):
|
||||
row.external_ids = external_ids
|
||||
|
||||
|
||||
# XenServer does not call interface-reconfigure on internal networks,
|
||||
# which is where the fail-mode would normally be set.
|
||||
def update_fail_mode(row):
|
||||
rec = get_network_by_bridge(row.name)
|
||||
if not rec:
|
||||
return
|
||||
|
||||
fail_mode = rec['other_config'].get('vswitch-controller-fail-mode')
|
||||
|
||||
if not fail_mode:
|
||||
pools = session.xenapi.pool.get_all()
|
||||
if len(pools) == 1:
|
||||
prec = session.xenapi.pool.get_record(pools[0])
|
||||
fail_mode = prec['other_config'].get(
|
||||
'vswitch-controller-fail-mode')
|
||||
|
||||
if fail_mode not in ['standalone', 'secure']:
|
||||
fail_mode = 'standalone'
|
||||
|
||||
row.verify("fail_mode")
|
||||
if row.fail_mode != fail_mode:
|
||||
row.fail_mode = fail_mode
|
||||
|
||||
|
||||
def update_in_band_mgmt(row):
|
||||
rec = get_network_by_bridge(row.name)
|
||||
if not rec:
|
||||
return
|
||||
|
||||
dib = rec['other_config'].get('vswitch-disable-in-band')
|
||||
|
||||
row.verify("other_config")
|
||||
other_config = row.other_config
|
||||
if dib and dib not in ['true', 'false']:
|
||||
vlog.warn('"%s" isn\'t a valid setting for '
|
||||
"other_config:disable-in-band on %s" % (dib, row.name))
|
||||
elif set_or_delete(other_config, 'disable-in-band', dib):
|
||||
row.other_config = other_config
|
||||
|
||||
|
||||
def main():
|
||||
global flush_cache, xapi_down
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("database", metavar="DATABASE",
|
||||
help="A socket on which ovsdb-server is listening.")
|
||||
parser.add_argument("--root-prefix", metavar="DIR", default='',
|
||||
help="Use DIR as alternate root directory"
|
||||
" (for testing).")
|
||||
|
||||
ovs.vlog.add_args(parser)
|
||||
ovs.daemon.add_args(parser)
|
||||
args = parser.parse_args()
|
||||
ovs.vlog.handle_args(args)
|
||||
ovs.daemon.handle_args(args)
|
||||
|
||||
remote = args.database
|
||||
schema_helper = ovs.db.idl.SchemaHelper()
|
||||
schema_helper.register_columns("Bridge", ["name", "external_ids",
|
||||
"other_config", "fail_mode"])
|
||||
schema_helper.register_columns("Interface", ["name", "external_ids"])
|
||||
idl = ovs.db.idl.Idl(remote, schema_helper)
|
||||
|
||||
ovs.daemon.daemonize()
|
||||
|
||||
ovs.unixctl.command_register("exit", "", 0, 0, unixctl_exit, None)
|
||||
ovs.unixctl.command_register("flush-cache", "", 0, 0, unixctl_flush_cache,
|
||||
None)
|
||||
error, unixctl_server = ovs.unixctl.server.UnixctlServer.create(None)
|
||||
if error:
|
||||
ovs.util.ovs_fatal(error, "could not create unixctl server", vlog)
|
||||
|
||||
# This daemon is usually started before XAPI, but to complete our
|
||||
# tasks, we need it. Wait here until it's up.
|
||||
cookie_file = args.root_prefix + "/var/run/xapi_init_complete.cookie"
|
||||
while not os.path.exists(cookie_file):
|
||||
time.sleep(1)
|
||||
|
||||
bridges = {} # Map from bridge name to nicira-bridge-id
|
||||
iface_ids = {} # Map from xs-vif-uuid to iface-id
|
||||
vm_ids = {} # Map from xs-vm-uuid to vm-id
|
||||
seqno = idl.change_seqno # Sequence number when we last processed the db
|
||||
while True:
|
||||
unixctl_server.run()
|
||||
if exiting:
|
||||
break
|
||||
|
||||
idl.run()
|
||||
if not xapi_down and not flush_cache and seqno == idl.change_seqno:
|
||||
poller = ovs.poller.Poller()
|
||||
unixctl_server.wait(poller)
|
||||
idl.wait(poller)
|
||||
poller.block()
|
||||
continue
|
||||
|
||||
if xapi_down:
|
||||
vlog.warn("Xapi is probably down. Retry again after a second.")
|
||||
time.sleep(1)
|
||||
xapi_down = False
|
||||
|
||||
if flush_cache:
|
||||
vlog.info("Flushing cache as the result of unixctl.")
|
||||
bridges = {}
|
||||
iface_ids = {}
|
||||
vm_ids = {}
|
||||
flush_cache = False
|
||||
seqno = idl.change_seqno
|
||||
|
||||
txn = ovs.db.idl.Transaction(idl)
|
||||
|
||||
new_bridges = {}
|
||||
for row in idl.tables["Bridge"].rows.values():
|
||||
bridge_id = bridges.get(row.name)
|
||||
if bridge_id is None:
|
||||
# Configure the new bridge.
|
||||
update_fail_mode(row)
|
||||
update_in_band_mgmt(row)
|
||||
|
||||
# Get the correct bridge_id, if we can.
|
||||
bridge_id = get_bridge_id(row.name)
|
||||
if bridge_id is None:
|
||||
xs_network_uuids = row.external_ids.get("xs-network-uuids")
|
||||
if xs_network_uuids:
|
||||
bridge_ids = xs_network_uuids.split(";")
|
||||
if len(bridge_ids) == 1:
|
||||
bridge_id = bridge_ids[0]
|
||||
else:
|
||||
bridge_id = get_single_bridge_id(bridge_ids,
|
||||
row.name)
|
||||
set_external_id(row, "bridge-id", bridge_id)
|
||||
|
||||
if bridge_id is not None:
|
||||
new_bridges[row.name] = bridge_id
|
||||
bridges = new_bridges
|
||||
|
||||
iface_by_name = {}
|
||||
for row in idl.tables["Interface"].rows.values():
|
||||
iface_by_name[row.name] = row
|
||||
|
||||
new_iface_ids = {}
|
||||
new_vm_ids = {}
|
||||
for row in idl.tables["Interface"].rows.values():
|
||||
# Match up paired vif and tap devices.
|
||||
if row.name.startswith("vif"):
|
||||
vif = row
|
||||
tap = iface_by_name.get("tap%s" % row.name[3:])
|
||||
elif row.name.startswith("tap"):
|
||||
tap = row
|
||||
vif = iface_by_name.get("vif%s" % row.name[3:])
|
||||
else:
|
||||
tap = vif = None
|
||||
|
||||
# Several tap external-ids need to be copied from the vif.
|
||||
if row == tap and vif:
|
||||
keys = ["attached-mac",
|
||||
"xs-network-uuid",
|
||||
"xs-vif-uuid",
|
||||
"xs-vm-uuid"]
|
||||
for k in keys:
|
||||
set_external_id(row, k, vif.external_ids.get(k))
|
||||
|
||||
# Map from xs-vif-uuid to iface-id.
|
||||
#
|
||||
# (A tap's xs-vif-uuid comes from its vif. That falls out
|
||||
# naturally from the copy loop above.)
|
||||
xvu = row.external_ids.get("xs-vif-uuid")
|
||||
if xvu:
|
||||
iface_id = (new_iface_ids.get(xvu)
|
||||
or iface_ids.get(xvu)
|
||||
or get_iface_id(row.name, xvu))
|
||||
new_iface_ids[xvu] = iface_id
|
||||
else:
|
||||
# No xs-vif-uuid therefore no iface-id.
|
||||
iface_id = None
|
||||
set_external_id(row, "iface-id", iface_id)
|
||||
|
||||
# Map from xs-vm-uuid to vm-id.
|
||||
xvmu = row.external_ids.get("xs-vm-uuid")
|
||||
if xvmu:
|
||||
vm_id = (new_vm_ids.get(xvmu)
|
||||
or vm_ids.get(xvmu)
|
||||
or get_vm_id(row.name, xvmu))
|
||||
new_vm_ids[xvmu] = vm_id
|
||||
else:
|
||||
vm_id = None
|
||||
set_external_id(row, "vm-id", vm_id)
|
||||
|
||||
# When there's a vif and a tap, the tap is active (used for
|
||||
# traffic). When there's just a vif, the vif is active.
|
||||
#
|
||||
# A tap on its own shouldn't happen, and we don't know
|
||||
# anything about other kinds of devices, so we don't use
|
||||
# an iface-status for those devices at all.
|
||||
if vif and tap:
|
||||
set_external_id(tap, "iface-status", "active")
|
||||
set_external_id(vif, "iface-status", "inactive")
|
||||
elif vif:
|
||||
set_external_id(vif, "iface-status", "active")
|
||||
else:
|
||||
set_external_id(row, "iface-status", None)
|
||||
iface_ids = new_iface_ids
|
||||
vm_ids = new_vm_ids
|
||||
|
||||
txn.add_comment("ovs-xapi-sync: Updating records from XAPI")
|
||||
txn.commit_block()
|
||||
|
||||
unixctl_server.close()
|
||||
idl.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
main()
|
||||
except SystemExit:
|
||||
# Let system.exit() calls complete normally
|
||||
raise
|
||||
except:
|
||||
vlog.exception("traceback")
|
||||
sys.exit(ovs.daemon.RESTART_EXIT_CODE)
|
@@ -1,24 +0,0 @@
|
||||
### Configuration options for openvswitch
|
||||
|
||||
# Copyright (C) 2009, 2010, 2011 Nicira, Inc.
|
||||
|
||||
# FORCE_COREFILES: If 'yes' then core files will be enabled.
|
||||
# FORCE_COREFILES=yes
|
||||
|
||||
# OVSDB_SERVER_PRIORITY: "nice" priority at which to run ovsdb-server.
|
||||
#
|
||||
# OVSDB_SERVER_PRIORITY=-10
|
||||
|
||||
# VSWITCHD_PRIORITY: "nice" priority at which to run ovs-vswitchd.
|
||||
# VSWITCHD_PRIORITY=-10
|
||||
|
||||
# VSWITCHD_MLOCKALL: Whether to pass ovs-vswitchd the --mlockall option.
|
||||
# This option should be set to "yes" or "no". The default is "yes".
|
||||
# Enabling this option can avoid networking interruptions due to
|
||||
# system memory pressure in extraordinary situations, such as multiple
|
||||
# concurrent VM import operations.
|
||||
# VSWITCHD_MLOCKALL=yes
|
||||
|
||||
# OVS_CTL_OPTS: Extra options to pass to ovs-ctl. This is, for example,
|
||||
# a suitable place to specify --ovs-vswitchd-wrapper=valgrind.
|
||||
# OVS_CTL_OPTS=
|
Reference in New Issue
Block a user