os-vif: introduce a ComputeInfo object to represent compute info

When a compute service is allocating ports with the network service,
it needs to pass across information about the capabilities of the
compute host. Currently each plugin provides information about its
own capabilities. This introduces a ComputeInfo class which,
initially, just contains a dict of PluginInfo classes.

With this change, there is a versioning constraint between the
compute and network hosts. The compute host must not pass the network
host a ComputeInfo object with a version that the network host
is unable to deserialize(). This implies that as a general rule the
version of os-vif on the network host must be at least as new as the
version on the compute hosts. During a live upgrade, the network
hosts must be upgraded before the compute hosts.

Change-Id: I7b51c97b5e03ffa51a56ceb3d4f97e188508379d
This commit is contained in:
Daniel P. Berrange 2016-02-17 12:27:02 +00:00
parent d3e9d8f273
commit 9686110d53
10 changed files with 225 additions and 51 deletions

View File

@ -109,3 +109,23 @@ def unplug(vif, instance_info):
LOG.error(_LE("Failed to unplug vif %(vif)s. Got error: %(err)s"),
vif=vif, err=err)
raise os_vif.exception.UnplugException(vif=vif, err=err)
def host_info():
"""
Get information about the host platform configuration to be
provided to the network manager. This will include information
about what plugins are installed in the host
:returns: a os_vif.host_info.HostInfo class instance
"""
if _EXT_MANAGER is None:
raise os_vif.exception.LibraryNotInitialized()
plugins = [
_EXT_MANAGER[name].obj.describe()
for name in _EXT_MANAGER.names()
]
return os_vif.objects.host_info.HostInfo(plugin_info=plugins)

View File

@ -51,6 +51,15 @@ class NoMatchingPlugin(ExceptionBase):
msg_fmt = _("No VIF plugin was found with the name %(plugin_name)s")
class NoMatchingVIFClass(ExceptionBase):
msg_fmt = _("No VIF class was found with the name %(vif_name)s")
class NoSupportedVIFVersion(ExceptionBase):
msg_fmt = _("VIF class %(vif_name)s versions %(got_versions)s "
"do not satisfy min=%(min_version)s max=%(max_version)s")
class PlugException(ExceptionBase):
msg_fmt = _("Failed to plug VIF %(vif)s. Got error: %(err)s")

View File

@ -13,6 +13,7 @@
def register_all():
__import__('os_vif.objects.fixed_ip')
__import__('os_vif.objects.host_info')
__import__('os_vif.objects.instance_info')
__import__('os_vif.objects.network')
__import__('os_vif.objects.route')

119
os_vif/objects/host_info.py Normal file
View File

@ -0,0 +1,119 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from oslo_versionedobjects import base
from oslo_versionedobjects import fields
from os_vif import exception
from os_vif.objects import base as osv_base
@base.VersionedObjectRegistry.register
class HostVIFInfo(osv_base.VersionedObject, base.ComparableVersionedObject):
"""
Class describing a VIF class and its supported versions
"""
VERSION = "1.0"
fields = {
# object name of the subclass of os_vif.objects.vif.VIF
"vif_object_name": fields.StringField(),
# String representing the earliest version of @name
# that the plugin understands
"min_version": fields.StringField(),
# String representing the latest version of @name
# that the plugin understands
"max_version": fields.StringField(),
}
def get_common_version(self):
def _vers_tuple(ver):
return tuple([int(x) for x in ver.split(".")])
reg = base.VersionedObjectRegistry.obj_classes()
if self.name not in reg:
raise exception.NoMatchingVIFClass(vif_name=self.name)
gotvers = []
for regobj in reg[self.name]:
gotvers.append(regobj.VERSION)
got = _vers_tuple(regobj.VERSION)
minwant = _vers_tuple(self.min_version)
maxwant = _vers_tuple(self.max_version)
if got >= minwant and got <= maxwant:
return regobj.VERSION
raise exception.NoSupportedVIFVersion(vif_name=self.name,
got_versions=",".join(gotvers),
min_version=self.min_version,
max_version=self.max_version)
@base.VersionedObjectRegistry.register
class HostPluginInfo(osv_base.VersionedObject,
base.ComparableVersionedObject):
"""
Class describing a plugin and its supported VIF classes
"""
VERSION = "1.0"
fields = {
# name of the plugin
"plugin_name": fields.StringField(),
# list of HostVIFInfo instances supported by the plugin
"vif_info": fields.ListOfObjectsField("HostVIFInfo"),
}
def has_vif(self, name):
for vif in self.vif_info:
if vif.name == name:
return True
return False
def get_vif(self, name):
for vif in self.vif_info:
if vif.name == name:
return vif
raise exception.NoMatchingVIFClass(vif_name=name)
@base.VersionedObjectRegistry.register
class HostInfo(osv_base.VersionedObject, base.ComparableVersionedObject):
"""
Class describing a host host and its supported plugin classes
"""
fields = {
# list of HostPluginInfo instances supported by the host host
"plugin_info": fields.ListOfObjectsField("HostPluginInfo"),
}
def has_plugin(self, name):
for plugin in self.plugin_info:
if name == plugin.name:
return True
return False
def get_plugin(self, name):
for plugin in self.plugin_info:
if name == plugin.name:
return plugin
raise exception.NoMatchingPlugin(plugin_name=name)

View File

@ -18,41 +18,6 @@ import six
CONF = cfg.CONF
class PluginVIFInfo(object):
"""
Class describing the plugin and the versions of VIF object it understands.
"""
def __init__(self, vif_class, min_version, max_version):
"""
Constructs the PluginInfo object.
:param vif_class: subclass of os_vif.objects.vif.VIF that is supported
:param min_version: String representing the earliest version of
@vif_class that the plugin understands.
:param_max_version: String representing the latest version of
@vif_class that the plugin understands.
"""
self.vif_class = vif_class
self.min_version = min_version
self.max_version = max_version
class PluginInfo(object):
"""
Class describing the plugin and the versions of VIF object it understands.
"""
def __init__(self, vif_info):
"""
Constructs the PluginInfo object.
:param vif_info: list of PluginVIFInfo instances supported by the
plugin
"""
self.vif_info = vif_info
@six.add_metaclass(abc.ABCMeta)
class PluginBase(object):
"""Base class for all VIF plugins."""
@ -75,7 +40,7 @@ class PluginBase(object):
Return an object that describes the plugin's supported vif types and
the earliest/latest known VIF object versions.
:returns: A `os_vif.plugin.PluginInfo` instance
:returns: A `os_vif.host_info.HostPluginInfo` instance
"""
@abc.abstractmethod

View File

@ -0,0 +1,51 @@
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from os_vif import objects
from os_vif.tests import base
class TestHostInfo(base.TestCase):
def test_serialization(self):
ciorig = objects.host_info.HostInfo(
plugin_info=[
objects.host_info.HostPluginInfo(
plugin_name="linux_brige",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name="VIFBridge",
min_version="1.0",
max_version="3.0"
),
]),
objects.host_info.HostPluginInfo(
plugin_name="ovs",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name="VIFBridge",
min_version="2.0",
max_version="7.0"
),
objects.host_info.HostVIFInfo(
vif_object_name="VIFOpenVSwitch",
min_version="1.0",
max_version="2.0"
),
])
])
json = ciorig.obj_to_primitive()
cinew = objects.host_info.HostInfo.obj_from_primitive(json)
self.assertEqual(ciorig, cinew)

View File

@ -95,6 +95,9 @@ class TestVIFS(base.TestCase):
port_profile=prof)
object_data = {
'HostInfo': '1.0-4dba5ce236ea2dc559de8764995dd247',
'HostPluginInfo': '1.0-5204e579864981c9891ecb5d1c9329f2',
'HostVIFInfo': '1.0-9866583de62ae23cc868ce45f402da6d',
'FixedIP': '1.0-d1a0ec7e7b6ce021a784c54d44cce009',
'FixedIPList': '1.0-15ecf022a68ddbb8c2a6739cfc9f8f5e',
'InstanceInfo': '1.0-84104d3435046b1a282ac8265ec2a976',

View File

@ -81,11 +81,13 @@ class LinuxBridgePlugin(plugin.PluginBase):
linux_net.configure(ipm)
def describe(self):
return plugin.PluginInfo(
[
plugin.PluginVIFInfo(
objects.vif.VIFBridge,
"1.0", "1.0")
return objects.host_info.HostPluginInfo(
plugin_name="linux_bridge",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFBridge.__name__,
min_version="1.0",
max_version="1.0")
])
def plug(self, vif, instance_info):

View File

@ -25,11 +25,13 @@ class OvsBridgePlugin(plugin.PluginBase):
"""An OVS VIF type that uses a standard Linux bridge for integration."""
def describe(self):
return plugin.PluginInfo(
[
plugin.PluginVIFInfo(
objects.vif.VIFOpenVSwitch,
"1.0", "1.0")
return objects.host_info.HostPluginInfo(
plugin_name="ovs",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFOpenVSwitch.__name__,
min_version="1.0",
max_version="1.0")
])
def plug(self, vif, instance_info):

View File

@ -57,11 +57,13 @@ class OvsHybridPlugin(plugin.PluginBase):
("qvo%s" % iface_id)[:OvsHybridPlugin.NIC_NAME_LEN])
def describe(self):
return plugin.PluginInfo(
[
plugin.PluginVIFInfo(
objects.vif.VIFBridge,
"1.0", "1.0")
return objects.host_info.HostPluginInfo(
plugin_name="ovs_hybrid",
vif_info=[
objects.host_info.HostVIFInfo(
vif_object_name=objects.vif.VIFBridge.__name__,
min_version="1.0",
max_version="1.0")
])
def plug(self, vif, instance_info):