Add NetLBFO network teaming

On Windows two main options are available to create network
adapter teams: NetLBFO (starting with Windows Server 2012) and
SET (starting with Windows Server 2016, requiring a VMSwitch).
This patch adds support for for NetLBFO.

Partially-Implements: blueprint json-network-config
Change-Id: Id80d24db0239acb8cb9d3ba5f9eaca1191957f1a
Co-Authored-By: Adrian Vladu <avladu@cloudbasesolutions.com>
This commit is contained in:
Alessandro Pilotti
2018-08-20 02:58:27 +03:00
parent 148855f74e
commit f99a8daabf
6 changed files with 607 additions and 0 deletions

View File

@@ -88,6 +88,14 @@ class BaseOSUtils(object):
broadcast, gateway, dnsnameservers): broadcast, gateway, dnsnameservers):
raise NotImplementedError() raise NotImplementedError()
def create_network_team(self, team_name, mode, load_balancing_algorithm,
members, mac_address, primary_nic_name=None,
primary_nic_vlan_id=None, lacp_timer=None):
raise NotImplementedError()
def add_network_team_nic(self, team_name, nic_name, vlan_id):
raise NotImplementedError()
def set_config_value(self, name, value, section=None): def set_config_value(self, name, value, section=None):
raise NotImplementedError() raise NotImplementedError()

View File

@@ -37,6 +37,7 @@ import winerror
from cloudbaseinit import exception from cloudbaseinit import exception
from cloudbaseinit.osutils import base from cloudbaseinit.osutils import base
from cloudbaseinit.utils import classloader
from cloudbaseinit.utils.windows import disk from cloudbaseinit.utils.windows import disk
from cloudbaseinit.utils.windows import network from cloudbaseinit.utils.windows import network
from cloudbaseinit.utils.windows import privilege from cloudbaseinit.utils.windows import privilege
@@ -415,6 +416,11 @@ class WindowsUtils(base.BaseOSUtils):
_FW_SCOPE_ALL = 0 _FW_SCOPE_ALL = 0
_FW_SCOPE_LOCAL_SUBNET = 1 _FW_SCOPE_LOCAL_SUBNET = 1
VER_NT_WORKSTATION = 1
def __init__(self):
self._network_team_manager = None
def reboot(self): def reboot(self):
with privilege.acquire_privilege(win32security.SE_SHUTDOWN_NAME): with privilege.acquire_privilege(win32security.SE_SHUTDOWN_NAME):
ret_val = advapi32.InitiateSystemShutdownExW( ret_val = advapi32.InitiateSystemShutdownExW(
@@ -867,6 +873,37 @@ class WindowsUtils(base.BaseOSUtils):
except wmi.x_wmi as exc: except wmi.x_wmi as exc:
raise exception.CloudbaseInitException(exc.com_error) raise exception.CloudbaseInitException(exc.com_error)
def _get_network_team_manager(self):
if self._network_team_manager:
return self._network_team_manager
team_managers = [
"cloudbaseinit.utils.windows.netlbfo.NetLBFOTeamManager",
]
cl = classloader.ClassLoader()
for class_name in team_managers:
try:
cls = cl.load_class(class_name)
if cls.is_available():
self._network_team_manager = cls()
return self._network_team_manager
except Exception as ex:
LOG.exception(ex)
raise exception.ItemNotFoundException(
"No network team manager available")
def create_network_team(self, team_name, mode, load_balancing_algorithm,
members, mac_address, primary_nic_name=None,
primary_nic_vlan_id=None, lacp_timer=None):
self._get_network_team_manager().create_team(
team_name, mode, load_balancing_algorithm, members, mac_address,
primary_nic_name, primary_nic_vlan_id, lacp_timer)
def add_network_team_nic(self, team_name, nic_name, vlan_id):
self._get_network_team_manager().add_team_nic(
team_name, nic_name, vlan_id)
def _get_config_key_name(self, section): def _get_config_key_name(self, section):
key_name = self._config_key key_name = self._config_key
if section: if section:
@@ -1187,6 +1224,9 @@ class WindowsUtils(base.BaseOSUtils):
"suite_mask": vi.wSuiteMask, "suite_mask": vi.wSuiteMask,
"product_type": vi.wProductType} "product_type": vi.wProductType}
def is_client_os(self):
return self.get_os_version()["product_type"] == self.VER_NT_WORKSTATION
def check_os_version(self, major, minor, build=0): def check_os_version(self, major, minor, build=0):
vi = Win32_OSVERSIONINFOEX_W() vi = Win32_OSVERSIONINFOEX_W()
vi.dwOSVersionInfoSize = ctypes.sizeof(Win32_OSVERSIONINFOEX_W) vi.dwOSVersionInfoSize = ctypes.sizeof(Win32_OSVERSIONINFOEX_W)

View File

@@ -61,6 +61,7 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
self._win32service_mock = mock.MagicMock() self._win32service_mock = mock.MagicMock()
self._winerror_mock = mock.MagicMock() self._winerror_mock = mock.MagicMock()
self._winerror_mock.ERROR_SERVICE_DOES_NOT_EXIST = 0x424 self._winerror_mock.ERROR_SERVICE_DOES_NOT_EXIST = 0x424
self._mi_mock = mock.MagicMock()
self._wmi_mock = mock.MagicMock() self._wmi_mock = mock.MagicMock()
self._wmi_mock.x_wmi = WMIError self._wmi_mock.x_wmi = WMIError
self._moves_mock = mock.MagicMock() self._moves_mock = mock.MagicMock()
@@ -81,6 +82,7 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
'win32netcon': self._win32netcon_mock, 'win32netcon': self._win32netcon_mock,
'win32service': self._win32service_mock, 'win32service': self._win32service_mock,
'winerror': self._winerror_mock, 'winerror': self._winerror_mock,
'mi': self._mi_mock,
'wmi': self._wmi_mock, 'wmi': self._wmi_mock,
'six.moves': self._moves_mock, 'six.moves': self._moves_mock,
'six.moves.xmlrpc_client': self._xmlrpc_client_mock, 'six.moves.xmlrpc_client': self._xmlrpc_client_mock,
@@ -1328,6 +1330,14 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
def test_check_os_version_fail(self): def test_check_os_version_fail(self):
self._test_check_os_version(ret_val=mock.Mock(), fail=True) self._test_check_os_version(ret_val=mock.Mock(), fail=True)
@mock.patch('cloudbaseinit.osutils.windows.WindowsUtils'
'.get_os_version')
def test_is_client_os(self, mock_get_os_version):
mock_get_os_version.return_value = {
"product_type": self._winutils.VER_NT_WORKSTATION}
self.assertEqual(True, self._winutils.is_client_os())
def _test_get_volume_label(self, ret_val): def _test_get_volume_label(self, ret_val):
label = mock.MagicMock() label = mock.MagicMock()
max_label_size = 261 max_label_size = 261
@@ -2630,3 +2640,48 @@ class TestWindowsUtils(testutils.CloudbaseInitTestBase):
self._win32api_mock.GetFileVersionInfo.assert_called_once_with( self._win32api_mock.GetFileVersionInfo.assert_called_once_with(
mock_path, '\\') mock_path, '\\')
self.assertIsNotNone(res) self.assertIsNotNone(res)
@mock.patch('cloudbaseinit.utils.windows.netlbfo.NetLBFOTeamManager')
def test_get_network_team_manager(self, mock_netlbfo_team_manager):
mock_netlbfo_team_manager.is_available.return_value = True
self.assertEqual(
mock_netlbfo_team_manager.return_value,
self._winutils._get_network_team_manager())
@mock.patch('cloudbaseinit.utils.windows.netlbfo.NetLBFOTeamManager')
def test_get_network_team_manager_not_found(self,
mock_netlbfo_team_manager):
mock_netlbfo_team_manager.is_available.return_value = False
self.assertRaises(
exception.ItemNotFoundException,
self._winutils._get_network_team_manager)
@mock.patch('cloudbaseinit.osutils.windows.WindowsUtils.'
'_get_network_team_manager')
def test_create_network_team(self, mock_get_network_team_manager):
mock_team_manager = mock_get_network_team_manager.return_value
self._winutils.create_network_team(
mock.sentinel.team_name, mock.sentinel.mode,
mock.sentinel.lb_algo, mock.sentinel.members,
mock.sentinel.mac, mock.sentinel.primary_name,
mock.sentinel.vlan_id, mock.sentinel.lacp_timer)
mock_team_manager.create_team.assert_called_once_with(
mock.sentinel.team_name, mock.sentinel.mode,
mock.sentinel.lb_algo, mock.sentinel.members,
mock.sentinel.mac, mock.sentinel.primary_name,
mock.sentinel.vlan_id, mock.sentinel.lacp_timer)
@mock.patch('cloudbaseinit.osutils.windows.WindowsUtils.'
'_get_network_team_manager')
def test_add_network_team_nic(self, mock_get_network_team_manager):
mock_team_manager = mock_get_network_team_manager.return_value
self._winutils.add_network_team_nic(
mock.sentinel.team_name, mock.sentinel.nic_name,
mock.sentinel.vlan_id)
mock_team_manager.add_team_nic.assert_called_once_with(
mock.sentinel.team_name, mock.sentinel.nic_name,
mock.sentinel.vlan_id)

View File

@@ -0,0 +1,262 @@
# Copyright (c) 2017 Cloudbase Solutions Srl
#
# 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 importlib
import unittest
try:
import unittest.mock as mock
except ImportError:
import mock
from cloudbaseinit import exception
from cloudbaseinit.models import network as network_model
MODPATH = "cloudbaseinit.utils.windows.netlbfo"
class NetLBFOTest(unittest.TestCase):
def setUp(self):
self._wmi_mock = mock.MagicMock()
self._mi_mock = mock.MagicMock()
self._module_patcher = mock.patch.dict(
'sys.modules', {
'wmi': self._wmi_mock,
'mi': self._mi_mock})
self._module_patcher.start()
self._netlbfo = importlib.import_module(MODPATH)
def tearDown(self):
self._module_patcher.stop()
@mock.patch(MODPATH + '.NetLBFOTeamManager._get_primary_adapter_name')
@mock.patch(MODPATH + '.NetLBFOTeamManager._add_team_member')
@mock.patch(MODPATH + '.NetLBFOTeamManager._set_primary_nic_vlan_id')
@mock.patch(MODPATH + '.NetLBFOTeamManager._wait_for_nic')
@mock.patch(MODPATH + '.NetLBFOTeamManager.delete_team')
def _test_create_team(self, mock_delete_team, mock_wait_for_nic,
mock_set_primary_nic_vlan_id, mock_add_team_member,
mock_get_primary_adapter_name, mode_not_found=False,
lb_algo_not_found=False, add_team_member_fail=False):
mock_get_primary_adapter_name.return_value = mock.sentinel.pri_nic_name
lacp_timer = network_model.BOND_LACP_RATE_FAST
members = [mock.sentinel.pri_nic_name, mock.sentinel.other_member]
conn = self._wmi_mock.WMI.return_value
mock_team = mock.Mock()
conn.MSFT_NetLbfoTeam.new.return_value = mock_team
if mode_not_found:
mode = "fake mode"
else:
mode = network_model.BOND_TYPE_8023AD
if lb_algo_not_found:
lb_algo = "fake lb algo"
else:
lb_algo = network_model.BOND_LB_ALGO_L2
if add_team_member_fail:
ex = exception.CloudbaseInitException
mock_add_team_member.side_effect = ex
if mode_not_found or lb_algo_not_found:
self.assertRaises(
exception.ItemNotFoundException,
self._netlbfo.NetLBFOTeamManager().create_team,
mock.sentinel.team_name, mode, lb_algo, members,
mock.sentinel.mac, mock.sentinel.pri_nic_name,
mock.sentinel.vlan_id, lacp_timer)
return
elif add_team_member_fail:
self.assertRaises(
exception.CloudbaseInitException,
self._netlbfo.NetLBFOTeamManager().create_team,
mock.sentinel.team_name, mode, lb_algo, members,
mock.sentinel.mac, mock.sentinel.pri_nic_name,
mock.sentinel.vlan_id, lacp_timer)
else:
self._netlbfo.NetLBFOTeamManager().create_team(
mock.sentinel.team_name, mode, lb_algo, members,
mock.sentinel.mac, mock.sentinel.pri_nic_name,
mock.sentinel.vlan_id, lacp_timer)
custom_options = [
{
u'name': u'TeamMembers',
u'value_type':
self._mi_mock.MI_ARRAY | self._mi_mock.MI_STRING,
u'value': [mock.sentinel.pri_nic_name]
},
{
u'name': u'TeamNicName',
u'value_type': self._mi_mock.MI_STRING,
u'value': mock.sentinel.pri_nic_name
}
]
operation_options = {u'custom_options': custom_options}
mock_team.put.assert_called_once_with(
operation_options=operation_options)
mock_add_team_member.assert_called_once_with(
conn, mock.sentinel.team_name, mock.sentinel.other_member)
if not add_team_member_fail:
mock_set_primary_nic_vlan_id.assert_called_once_with(
conn, mock.sentinel.team_name, mock.sentinel.vlan_id)
mock_wait_for_nic.assert_called_once_with(
mock.sentinel.pri_nic_name)
else:
mock_delete_team.assert_called_once_with(mock.sentinel.team_name)
def test_create_team(self):
self._test_create_team()
def test_create_team_mode_not_found(self):
self._test_create_team(mode_not_found=True)
def test_create_team_mode_lb_algo_not_found(self):
self._test_create_team(lb_algo_not_found=True)
def test_create_team_add_team_member_fail(self):
self._test_create_team(add_team_member_fail=True)
def test_delete_team(self):
conn = self._wmi_mock.WMI.return_value
mock_team = mock.Mock()
conn.MSFT_NetLbfoTeam.return_value = [mock_team]
self._netlbfo.NetLBFOTeamManager().delete_team(mock.sentinel.team_name)
conn.MSFT_NetLbfoTeam.assert_called_once_with(
name=mock.sentinel.team_name)
mock_team.Delete_.assert_called_once_with()
@mock.patch(MODPATH + '.NetLBFOTeamManager._wait_for_nic')
def test_add_team_nic(self, mock_wait_for_nic):
conn = self._wmi_mock.WMI.return_value
mock_team_nic = mock.Mock()
conn.MSFT_NetLbfoTeamNIC.new.return_value = mock_team_nic
self._netlbfo.NetLBFOTeamManager().add_team_nic(
mock.sentinel.team_name, mock.sentinel.nic_name,
mock.sentinel.vlan_id)
self.assertEqual(mock.sentinel.team_name, mock_team_nic.Team)
self.assertEqual(mock.sentinel.nic_name, mock_team_nic.Name)
self.assertEqual(mock.sentinel.vlan_id, mock_team_nic.VlanID)
mock_team_nic.put.assert_called_once_with()
mock_wait_for_nic.assert_called_once_with(mock_team_nic.Name)
@mock.patch('cloudbaseinit.osutils.factory.get_os_utils')
def test_is_available(self, mock_get_os_utils):
os_utils = mock_get_os_utils.return_value
os_utils.check_os_version.return_value = True
os_utils.is_client_os.return_value = False
with mock.patch('sys.platform', 'win32'):
self.assertEqual(
True, self._netlbfo.NetLBFOTeamManager.is_available())
@mock.patch('time.sleep')
def test_wait_for_nic(self, mock_sleep):
conn = self._wmi_mock.WMI.return_value
conn.Win32_NetworkAdapter.side_effect = [
[], [mock.sentinel.net_adapter]]
self._netlbfo.NetLBFOTeamManager()._wait_for_nic(
mock.sentinel.nic_name)
conn.Win32_NetworkAdapter.assert_has_calls([
mock.call(NetConnectionID=mock.sentinel.nic_name),
mock.call(NetConnectionID=mock.sentinel.nic_name)])
mock_sleep.assert_called_once_with(1)
def test_set_primary_nic_vlan_id(self):
conn = mock.Mock()
mock_team_nic = mock.Mock()
conn.MSFT_NetLbfoTeamNIC.return_value = [mock_team_nic]
self._netlbfo.NetLBFOTeamManager()._set_primary_nic_vlan_id(
conn, mock.sentinel.team_name, mock.sentinel.vlan_id)
custom_options = [{
u'name': u'VlanID',
u'value_type': self._mi_mock.MI_UINT32,
u'value': mock.sentinel.vlan_id
}]
operation_options = {u'custom_options': custom_options}
mock_team_nic.put.assert_called_once_with(
operation_options=operation_options)
def test_add_team_member(self):
conn = mock.Mock()
mock_team_member = mock.Mock()
conn.MSFT_NetLbfoTeamMember.new.return_value = mock_team_member
self._netlbfo.NetLBFOTeamManager()._add_team_member(
conn, mock.sentinel.team_name, mock.sentinel.team_member)
custom_options = [{
u'name': u'Name',
u'value_type': self._mi_mock.MI_STRING,
u'value': mock.sentinel.team_member
}]
operation_options = {u'custom_options': custom_options}
mock_team_member.put.assert_called_once_with(
operation_options=operation_options)
self.assertEqual(mock.sentinel.team_name, mock_team_member.Team)
def _test_get_primary_adapter_name(self, mac_not_found=False,
member_not_found=False):
mock_members = [mock.sentinel.team_member]
conn = self._wmi_mock.WMI.return_value
if mac_not_found:
conn.Win32_NetworkAdapter.return_value = []
else:
conn.Win32_NetworkAdapter.return_value = [
mock.sentinel.net_adapter]
if member_not_found:
net_conn_id = mock.sentinel.something_else
else:
net_conn_id = mock.sentinel.team_member
mock.sentinel.net_adapter.NetConnectionID = net_conn_id
if mac_not_found or member_not_found:
self.assertRaises(
exception.ItemNotFoundException,
self._netlbfo.NetLBFOTeamManager()._get_primary_adapter_name,
mock_members, mock.sentinel.mac)
else:
self.assertEqual(
mock.sentinel.team_member,
self._netlbfo.NetLBFOTeamManager()._get_primary_adapter_name(
mock_members, mock.sentinel.mac))
conn.Win32_NetworkAdapter.assert_called_once_with(
MACAddress=mock.sentinel.mac)
def test_get_primary_adapter_name(self):
self._test_get_primary_adapter_name()
def test_get_primary_adapter_name_mac_not_found(self):
self._test_get_primary_adapter_name(mac_not_found=True)
def test_get_primary_adapter_name_member_not_found(self):
self._test_get_primary_adapter_name(member_not_found=True)

View File

@@ -0,0 +1,34 @@
# Copyright 2018 Cloudbase Solutions Srl
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class BaseNetworkTeamManager(object):
@abc.abstractmethod
def create_team(self, team_name, mode, load_balancing_algorithm,
members, mac_address, primary_nic_name=None,
primary_nic_vlan_id=None, lacp_timer=None):
pass
@abc.abstractmethod
def add_team_nic(self, team_name, nic_name, vlan_id):
pass
@abc.abstractmethod
def delete_team(self, name):
pass

View File

@@ -0,0 +1,208 @@
# Copyright 2018 Cloudbase Solutions Srl
#
# 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 sys
import time
import mi
from oslo_log import log as oslo_logging
import wmi
from cloudbaseinit import exception
from cloudbaseinit.models import network as network_model
from cloudbaseinit.osutils import factory as osutils_factory
from cloudbaseinit.utils import network_team
LBFO_TEAM_MODE_STATIC = 0
LBFO_TEAM_MODE_SWITCH_INDEPENDENT = 1
LBFO_TEAM_MODE_LACP = 2
LBFO_TEAM_ALGORITHM_TRANSPORT_PORTS = 0
LBFO_TEAM_ALGORITHM_IP_ADDRESSES = 2
LBFO_TEAM_ALGORITHM_MAC_ADDRESSES = 3
LBFO_TEAM_ALGORITHM_HYPERV_PORT = 4
LBFO_TEAM_ALGORITHM_DYNAMIC = 5
LBFO_TEST_LACP_TIMER_SLOW = 0
LBFO_TEST_LACP_TIMER_FAST = 1
NETWORK_MODEL_TEAM_MODE_MAP = {
network_model.BOND_TYPE_8023AD: LBFO_TEAM_MODE_LACP,
network_model.BOND_TYPE_BALANCE_RR: LBFO_TEAM_MODE_STATIC,
network_model.BOND_TYPE_ACTIVE_BACKUP: LBFO_TEAM_MODE_SWITCH_INDEPENDENT,
network_model.BOND_TYPE_BALANCE_XOR: LBFO_TEAM_MODE_STATIC,
network_model.BOND_TYPE_BALANCE_TLB: LBFO_TEAM_MODE_SWITCH_INDEPENDENT,
network_model.BOND_TYPE_BALANCE_ALB: LBFO_TEAM_MODE_SWITCH_INDEPENDENT,
}
NETWORK_MODEL_LB_ALGO_MAP = {
network_model.BOND_LB_ALGO_L2: LBFO_TEAM_ALGORITHM_MAC_ADDRESSES,
network_model.BOND_LB_ALGO_L2_L3: LBFO_TEAM_ALGORITHM_IP_ADDRESSES,
network_model.BOND_LB_ALGO_L3_L4: LBFO_TEAM_ALGORITHM_TRANSPORT_PORTS,
network_model.BOND_LB_ENCAP_L2_L3: LBFO_TEAM_ALGORITHM_IP_ADDRESSES,
network_model.BOND_LB_ENCAP_L3_L4: LBFO_TEAM_ALGORITHM_TRANSPORT_PORTS,
}
NETWORK_MODEL_LACP_RATE_MAP = {
network_model.BOND_LACP_RATE_FAST: LBFO_TEST_LACP_TIMER_FAST,
network_model.BOND_LACP_RATE_SLOW: LBFO_TEST_LACP_TIMER_SLOW,
}
LOG = oslo_logging.getLogger(__name__)
class NetLBFOTeamManager(network_team.BaseNetworkTeamManager):
@staticmethod
def _get_primary_adapter_name(members, mac_address):
conn = wmi.WMI(moniker='root/cimv2')
adapters = conn.Win32_NetworkAdapter(MACAddress=mac_address)
if not adapters:
raise exception.ItemNotFoundException(
"No adapter with MAC address \"%s\" found" % mac_address)
primary_adapter_name = adapters[0].NetConnectionID
if primary_adapter_name not in members:
raise exception.ItemNotFoundException(
"Adapter \"%s\" not found in members" % primary_adapter_name)
return primary_adapter_name
@staticmethod
def _add_team_member(conn, team_name, member):
team_member = conn.MSFT_NetLbfoTeamMember.new()
team_member.Team = team_name
custom_options = [{
u'name': u'Name',
u'value_type': mi.MI_STRING,
u'value': member
}]
operation_options = {u'custom_options': custom_options}
team_member.put(operation_options=operation_options)
@staticmethod
def _set_primary_nic_vlan_id(conn, team_name, vlan_id):
team_nic = conn.MSFT_NetLbfoTeamNIC(Team=team_name, Primary=True)[0]
custom_options = [{
u'name': u'VlanID',
u'value_type': mi.MI_UINT32,
u'value': vlan_id
}]
operation_options = {u'custom_options': custom_options}
team_nic.put(operation_options=operation_options)
def create_team(self, team_name, mode, load_balancing_algorithm,
members, mac_address, primary_nic_name=None,
primary_nic_vlan_id=None, lacp_timer=None):
conn = wmi.WMI(moniker='root/standardcimv2')
primary_adapter_name = self._get_primary_adapter_name(
members, mac_address)
teaming_mode = NETWORK_MODEL_TEAM_MODE_MAP.get(mode)
if teaming_mode is None:
raise exception.ItemNotFoundException(
"Unsupported teaming mode: %s" % mode)
if load_balancing_algorithm is None:
lb_algo = LBFO_TEAM_ALGORITHM_DYNAMIC
else:
lb_algo = NETWORK_MODEL_LB_ALGO_MAP.get(
load_balancing_algorithm)
if lb_algo is None:
raise exception.ItemNotFoundException(
"Unsupported LB algorithm: %s" % load_balancing_algorithm)
team = conn.MSFT_NetLbfoTeam.new()
team.Name = team_name
team.TeamingMode = teaming_mode
team.LoadBalancingAlgorithm = lb_algo
if lacp_timer is not None and team.TeamingMode == LBFO_TEAM_MODE_LACP:
team.LacpTimer = NETWORK_MODEL_LACP_RATE_MAP[lacp_timer]
nic_name = primary_nic_name or team_name
custom_options = [
{
u'name': u'TeamMembers',
u'value_type': mi.MI_ARRAY | mi.MI_STRING,
u'value': [primary_adapter_name]
},
{
u'name': u'TeamNicName',
u'value_type': mi.MI_STRING,
u'value': nic_name
}
]
operation_options = {u'custom_options': custom_options}
team.put(operation_options=operation_options)
try:
for member in members:
if member != primary_adapter_name:
self._add_team_member(conn, team_name, member)
if primary_nic_vlan_id is not None:
self._set_primary_nic_vlan_id(
conn, team_name, primary_nic_vlan_id)
if primary_nic_name != team_name or primary_nic_vlan_id is None:
# TODO(alexpilotti): query the new nic name
# When the nic name equals the bond name and a VLAN ID is set,
# the nick name is changed.
self._wait_for_nic(nic_name)
except Exception as ex:
self.delete_team(team_name)
raise ex
@staticmethod
def _wait_for_nic(nic_name):
conn = wmi.WMI(moniker='//./root/cimv2')
max_count = 100
i = 0
while True:
if len(conn.Win32_NetworkAdapter(NetConnectionID=nic_name)):
break
else:
i += 1
if i >= max_count:
raise exception.ItemNotFoundException(
"Cannot find team NIC: %s" % nic_name)
LOG.debug("Waiting for team NIC: %s", nic_name)
time.sleep(1)
def add_team_nic(self, team_name, nic_name, vlan_id):
conn = wmi.WMI(moniker='root/standardcimv2')
team_nic = conn.MSFT_NetLbfoTeamNIC.new()
team_nic.Team = team_name
team_nic.Name = nic_name
team_nic.VlanID = vlan_id
team_nic.put()
# Ensure that the NIC is visible in the OS before returning
self._wait_for_nic(nic_name)
def delete_team(self, team_name):
conn = wmi.WMI(moniker='root/standardcimv2')
teams = conn.MSFT_NetLbfoTeam(name=team_name)
if not teams:
raise exception.ItemNotFoundException(
"Team not found: %s" % team_name)
teams[0].Delete_()
@classmethod
def is_available(cls):
osutils = osutils_factory.get_os_utils()
return (sys.platform == 'win32' and osutils.check_os_version(6, 2) and
not osutils.is_client_os())