Merge "Collect NIC name given by BIOS"

This commit is contained in:
Jenkins 2017-05-22 11:35:41 +00:00 committed by Gerrit Code Review
commit 2deef86f32
10 changed files with 198 additions and 19 deletions

View File

@ -118,11 +118,11 @@ fields:
``interfaces`` ``interfaces``
list of network interfaces with fields: ``name``, ``mac_address``, list of network interfaces with fields: ``name``, ``mac_address``,
``ipv4_address``, ``lldp``, ``vendor`` and ``product``. ``ipv4_address``, ``lldp``, ``vendor``, ``product``, and optionally
If configuration option ``collect_lldp`` is set to True the ``lldp`` ``biosdevname``(BIOS given NIC name). If configuration option
field will be populated by a list of type-length-value (TLV) fields ``collect_lldp`` is set to True the ``lldp`` field will be populated
retrieved using the Link Layer Discovery Protocol (LLDP). by a list of type-length-value(TLV) fields retrieved using the
Link Layer Discovery Protocol (LLDP).
``system_vendor`` ``system_vendor``
system vendor information from SMBIOS as reported by ``dmidecode``: system vendor information from SMBIOS as reported by ``dmidecode``:

View File

@ -105,3 +105,13 @@ To provide other public SSH key, export path to it in your shell before
building tinyipa as follows:: building tinyipa as follows::
export SSH_PUBLIC_KEY=<full-path-to-public-key> export SSH_PUBLIC_KEY=<full-path-to-public-key>
Enabling biosdevname in the ramdisk
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to collect BIOS given names of NICs in the inventory, set
``TINYIPA_REQUIRE_BIOSDEVNAME`` variable in your shell before building the
tinyipa::
export TINYIPA_REQUIRE_BIOSDEVNAME=true

View File

@ -6,6 +6,7 @@ source ${WORKDIR}/tc-mirror.sh
BUILDDIR="$WORKDIR/tinyipabuild" BUILDDIR="$WORKDIR/tinyipabuild"
BUILD_AND_INSTALL_TINYIPA=${BUILD_AND_INSTALL_TINYIPA:-false} BUILD_AND_INSTALL_TINYIPA=${BUILD_AND_INSTALL_TINYIPA:-false}
TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-} TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-}
TINYIPA_REQUIRE_BIOSDEVNAME=${TINYIPA_REQUIRE_BIOSDEVNAME:-false}
CHROOT_PATH="/tmp/overides:/usr/local/sbin:/usr/local/bin:/apps/bin:/usr/sbin:/usr/bin:/sbin:/bin" CHROOT_PATH="/tmp/overides:/usr/local/sbin:/usr/local/bin:/apps/bin:/usr/sbin:/usr/bin:/sbin:/bin"
CHROOT_CMD="sudo chroot $BUILDDIR /usr/bin/env -i PATH=$CHROOT_PATH http_proxy=$http_proxy https_proxy=$https_proxy no_proxy=$no_proxy" CHROOT_CMD="sudo chroot $BUILDDIR /usr/bin/env -i PATH=$CHROOT_PATH http_proxy=$http_proxy https_proxy=$https_proxy no_proxy=$no_proxy"
@ -57,9 +58,12 @@ sudo sh -c "echo $TINYCORE_MIRROR_URL > $BUILDDIR/opt/tcemirror"
# Download get-pip into ramdisk # Download get-pip into ramdisk
( cd "$BUILDDIR/tmp" && wget https://bootstrap.pypa.io/get-pip.py ) ( cd "$BUILDDIR/tmp" && wget https://bootstrap.pypa.io/get-pip.py )
# Download TGT and Qemu-utils source # Download TGT, Qemu-utils, and Biosdevname source
clone_and_checkout "https://github.com/fujita/tgt.git" "${BUILDDIR}/tmp/tgt" "v1.0.62" clone_and_checkout "https://github.com/fujita/tgt.git" "${BUILDDIR}/tmp/tgt" "v1.0.62"
clone_and_checkout "https://github.com/qemu/qemu.git" "${BUILDDIR}/tmp/qemu" "v2.5.0" clone_and_checkout "https://github.com/qemu/qemu.git" "${BUILDDIR}/tmp/qemu" "v2.5.0"
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
wget -N -O - https://linux.dell.com/biosdevname/biosdevname-0.7.2/biosdevname-0.7.2.tar.gz | tar -xz -C "${BUILDDIR}/tmp" -f -
fi
# Create directory for python local mirror # Create directory for python local mirror
mkdir -p "$BUILDDIR/tmp/localpip" mkdir -p "$BUILDDIR/tmp/localpip"
@ -114,3 +118,11 @@ cd $WORKDIR/build_files && mksquashfs $BUILDDIR/tmp/qemu-utils qemu-utils.tcz &&
# Create qemu-utils.tcz.dep # Create qemu-utils.tcz.dep
echo "glib2.tcz" > qemu-utils.tcz.dep echo "glib2.tcz" > qemu-utils.tcz.dep
# Build biosdevname
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
rm -rf $WORKDIR/build_files/biosdevname.tcz
$CHROOT_CMD /bin/sh -c "cd /tmp/biosdevname-* && ./configure && make && make install DESTDIR=/tmp/biosdevname-installed"
find $BUILDDIR/tmp/biosdevname-installed/ -type f -executable | xargs file | awk -F ':' '/ELF/ {print $1}' | sudo xargs strip
cd $WORKDIR/build_files && mksquashfs $BUILDDIR/tmp/biosdevname-installed biosdevname.tcz && md5sum biosdevname.tcz > biosdevname.tcz.md5.txt
fi

View File

@ -7,6 +7,8 @@ hdparm.tcz
parted.tcz parted.tcz
python.tcz python.tcz
python-dev.tcz python-dev.tcz
pciutils.tcz
libpci-dev.tcz
raid-dm-4.2.9-tinycore64.tcz raid-dm-4.2.9-tinycore64.tcz
scsi-4.2.9-tinycore64.tcz scsi-4.2.9-tinycore64.tcz
udev-lib.tcz udev-lib.tcz

View File

@ -7,6 +7,7 @@ iproute2.tcz
parted.tcz parted.tcz
popt.tcz popt.tcz
python.tcz python.tcz
pciutils.tcz
raid-dm-4.2.9-tinycore64.tcz raid-dm-4.2.9-tinycore64.tcz
scsi-4.2.9-tinycore64.tcz scsi-4.2.9-tinycore64.tcz
udev-lib.tcz udev-lib.tcz

View File

@ -10,6 +10,7 @@ TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-}
ENABLE_SSH=${ENABLE_SSH:-false} ENABLE_SSH=${ENABLE_SSH:-false}
SSH_PUBLIC_KEY=${SSH_PUBLIC_KEY:-} SSH_PUBLIC_KEY=${SSH_PUBLIC_KEY:-}
PYOPTIMIZE_TINYIPA=${PYOPTIMIZE_TINYIPA:-true} PYOPTIMIZE_TINYIPA=${PYOPTIMIZE_TINYIPA:-true}
TINYIPA_REQUIRE_BIOSDEVNAME=${TINYIPA_REQUIRE_BIOSDEVNAME:-false}
TC=1001 TC=1001
STAFF=50 STAFF=50
@ -86,6 +87,9 @@ echo "tc" | $CHROOT_CMD tee -a /etc/sysconfig/tcuser
cp $WORKDIR/build_files/tgt.* $FINALDIR/tmp/builtin/optional cp $WORKDIR/build_files/tgt.* $FINALDIR/tmp/builtin/optional
cp $WORKDIR/build_files/qemu-utils.* $FINALDIR/tmp/builtin/optional cp $WORKDIR/build_files/qemu-utils.* $FINALDIR/tmp/builtin/optional
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
cp $WORKDIR/build_files/biosdevname.* $FINALDIR/tmp/builtin/optional
fi
# Mount /proc for chroot commands # Mount /proc for chroot commands
sudo mount --bind /proc $FINALDIR/proc sudo mount --bind /proc $FINALDIR/proc
@ -123,6 +127,9 @@ fi
$TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/tgt.tcz $TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/tgt.tcz
$TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/qemu-utils.tcz $TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/qemu-utils.tcz
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
$TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/biosdevname.tcz
fi
# Ensure tinyipa picks up installed kernel modules # Ensure tinyipa picks up installed kernel modules
$CHROOT_CMD depmod -a `$WORKDIR/build_files/fakeuname -r` $CHROOT_CMD depmod -a `$WORKDIR/build_files/fakeuname -r`

View File

@ -218,10 +218,11 @@ class BlockDevice(encoding.SerializableComparable):
class NetworkInterface(encoding.SerializableComparable): class NetworkInterface(encoding.SerializableComparable):
serializable_fields = ('name', 'mac_address', 'ipv4_address', serializable_fields = ('name', 'mac_address', 'ipv4_address',
'has_carrier', 'lldp', 'vendor', 'product', 'has_carrier', 'lldp', 'vendor', 'product',
'client_id') 'client_id', 'biosdevname')
def __init__(self, name, mac_addr, ipv4_address=None, has_carrier=True, def __init__(self, name, mac_addr, ipv4_address=None, has_carrier=True,
lldp=None, vendor=None, product=None, client_id=None): lldp=None, vendor=None, product=None, client_id=None,
biosdevname=None):
self.name = name self.name = name
self.mac_address = mac_addr self.mac_address = mac_addr
self.ipv4_address = ipv4_address self.ipv4_address = ipv4_address
@ -229,6 +230,7 @@ class NetworkInterface(encoding.SerializableComparable):
self.lldp = lldp self.lldp = lldp
self.vendor = vendor self.vendor = vendor
self.product = product self.product = product
self.biosdevname = biosdevname
# client_id is used for InfiniBand only. we calculate the DHCP # client_id is used for InfiniBand only. we calculate the DHCP
# client identifier Option to allow DHCP to work over InfiniBand. # client identifier Option to allow DHCP to work over InfiniBand.
# see https://tools.ietf.org/html/rfc4390 # see https://tools.ietf.org/html/rfc4390
@ -531,11 +533,41 @@ class GenericHardwareManager(HardwareManager):
ipv4_address=self.get_ipv4_addr(interface_name), ipv4_address=self.get_ipv4_addr(interface_name),
has_carrier=netutils.interface_has_carrier(interface_name), has_carrier=netutils.interface_has_carrier(interface_name),
vendor=_get_device_info(interface_name, 'net', 'vendor'), vendor=_get_device_info(interface_name, 'net', 'vendor'),
product=_get_device_info(interface_name, 'net', 'device')) product=_get_device_info(interface_name, 'net', 'device'),
biosdevname=self.get_bios_given_nic_name(interface_name))
def get_ipv4_addr(self, interface_id): def get_ipv4_addr(self, interface_id):
return netutils.get_ipv4_addr(interface_id) return netutils.get_ipv4_addr(interface_id)
def get_bios_given_nic_name(self, interface_name):
"""Collect the BIOS given NICs name.
This function uses the biosdevname utility to collect the BIOS given
name of network interfaces.
The collected data is added to the network interface inventory with an
extra field named ``biosdevname``.
:param interface_name: list of names of node's interfaces.
:return: the BIOS given NIC name of node's interfaces or default
as None.
"""
try:
stdout, _ = utils.execute('biosdevname', '-i',
interface_name)
return stdout.rstrip('\n')
except OSError:
LOG.warning("Executable 'biosdevname' not found")
return
except processutils.ProcessExecutionError as e:
# NOTE(alezil) biosdevname returns 4 if running in a
# virtual machine.
if e.exit_code == 4:
LOG.info('The system is a virtual machine, so biosdevname '
'utility does not provide names for virtual NICs.')
else:
LOG.warning('Biosdevname returned exit code %s', e.exit_code)
def _is_device(self, interface_name): def _is_device(self, interface_name):
device_path = '{}/class/net/{}/device'.format(self.sys_path, device_path = '{}/class/net/{}/device'.format(self.sys_path,
interface_name) interface_name)

View File

@ -385,15 +385,14 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_dispatch.assert_has_calls(expected_dispatch_calls) mock_dispatch.assert_has_calls(expected_dispatch_calls)
mock_sleep.assert_has_calls(expected_sleep_calls) mock_sleep.assert_has_calls(expected_sleep_calls)
@mock.patch('ironic_python_agent.hardware_managers.cna._detect_cna_card', @mock.patch.object(hardware, 'load_managers', autospec=True)
autospec=True)
@mock.patch.object(time, 'sleep', autospec=True) @mock.patch.object(time, 'sleep', autospec=True)
@mock.patch('wsgiref.simple_server.make_server', autospec=True) @mock.patch.object(agent.IronicPythonAgent, '_wait_for_interface',
@mock.patch.object(hardware, '_check_for_iscsi', autospec=True)
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
autospec=True) autospec=True)
def test_run_with_sleep(self, mock_check_for_iscsi, mock_list_hardware, @mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
mock_make_server, mock_sleep, mock_cna): @mock.patch('wsgiref.simple_server.make_server', autospec=True)
def test_run_with_sleep(self, mock_make_server, mock_dispatch,
mock_load_managers, mock_sleep, mock_wait):
CONF.set_override('inspection_callback_url', '', enforce_type=True) CONF.set_override('inspection_callback_url', '', enforce_type=True)
wsgi_server = mock_make_server.return_value wsgi_server = mock_make_server.return_value
wsgi_server.start.side_effect = KeyboardInterrupt() wsgi_server.start.side_effect = KeyboardInterrupt()
@ -409,7 +408,6 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
'heartbeat_timeout': 300 'heartbeat_timeout': 300
} }
} }
mock_cna.return_value = False
self.agent.run() self.agent.run()
listen_addr = agent.Host('192.0.2.1', 9999) listen_addr = agent.Host('192.0.2.1', 9999)
@ -422,7 +420,9 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.heartbeater.start.assert_called_once_with() self.agent.heartbeater.start.assert_called_once_with()
mock_sleep.assert_called_once_with(10) mock_sleep.assert_called_once_with(10)
self.assertTrue(mock_check_for_iscsi.called) self.assertTrue(mock_load_managers.called)
self.assertTrue(mock_wait.called)
mock_dispatch.assert_called_once_with('list_hardware_info')
def test_async_command_success(self): def test_async_command_success(self):
result = base.AsyncCommandResult('foo_command', {'fail': False}, result = base.AsyncCommandResult('foo_command', {'fail': False},

View File

@ -378,7 +378,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
@mock.patch('os.listdir', autospec=True) @mock.patch('os.listdir', autospec=True)
@mock.patch('os.path.exists', autospec=True) @mock.patch('os.path.exists', autospec=True)
@mock.patch('six.moves.builtins.open', autospec=True) @mock.patch('six.moves.builtins.open', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test_list_network_interfaces(self, def test_list_network_interfaces(self,
mocked_execute,
mocked_open, mocked_open,
mocked_exists, mocked_exists,
mocked_listdir, mocked_listdir,
@ -394,6 +396,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mocked_ifaddresses.return_value = { mocked_ifaddresses.return_value = {
netifaces.AF_INET: [{'addr': '192.168.1.2'}] netifaces.AF_INET: [{'addr': '192.168.1.2'}]
} }
mocked_execute.return_value = ('em0\n', '')
interfaces = self.hardware.list_network_interfaces() interfaces = self.hardware.list_network_interfaces()
self.assertEqual(1, len(interfaces)) self.assertEqual(1, len(interfaces))
self.assertEqual('eth0', interfaces[0].name) self.assertEqual('eth0', interfaces[0].name)
@ -401,6 +404,92 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('192.168.1.2', interfaces[0].ipv4_address) self.assertEqual('192.168.1.2', interfaces[0].ipv4_address)
self.assertIsNone(interfaces[0].lldp) self.assertIsNone(interfaces[0].lldp)
self.assertTrue(interfaces[0].has_carrier) self.assertTrue(interfaces[0].has_carrier)
self.assertEqual('em0', interfaces[0].biosdevname)
@mock.patch('ironic_python_agent.hardware._get_managers', autospec=True)
@mock.patch('netifaces.ifaddresses', autospec=True)
@mock.patch('os.listdir', autospec=True)
@mock.patch('os.path.exists', autospec=True)
@mock.patch('six.moves.builtins.open', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test_list_network_interfaces_with_biosdevname(self,
mocked_execute,
mocked_open,
mocked_exists,
mocked_listdir,
mocked_ifaddresses,
mocked_get_managers):
mocked_get_managers.return_value = [hardware.GenericHardwareManager()]
mocked_listdir.return_value = ['lo', 'eth0']
mocked_exists.side_effect = [False, True]
mocked_open.return_value.__enter__ = lambda s: s
mocked_open.return_value.__exit__ = mock.Mock()
read_mock = mocked_open.return_value.read
read_mock.side_effect = ['00:0c:29:8c:11:b1\n', '1']
mocked_ifaddresses.return_value = {
netifaces.AF_INET: [{'addr': '192.168.1.2'}]
}
mocked_execute.return_value = ('em0\n', '')
interfaces = self.hardware.list_network_interfaces()
self.assertEqual(1, len(interfaces))
self.assertEqual('eth0', interfaces[0].name)
self.assertEqual('00:0c:29:8c:11:b1', interfaces[0].mac_address)
self.assertEqual('192.168.1.2', interfaces[0].ipv4_address)
self.assertIsNone(interfaces[0].lldp)
self.assertTrue(interfaces[0].has_carrier)
self.assertEqual('em0', interfaces[0].biosdevname)
@mock.patch.object(utils, 'execute', autospec=True)
def test_get_bios_given_nic_name_ok(self, mock_execute):
interface_name = 'eth0'
mock_execute.return_value = ('em0\n', '')
result = self.hardware.get_bios_given_nic_name(interface_name)
self.assertEqual('em0', result)
mock_execute.assert_called_once_with('biosdevname', '-i',
interface_name)
@mock.patch.object(utils, 'execute', autospec=True)
def test_get_bios_given_nic_name_oserror(self, mock_execute):
interface_name = 'eth0'
mock_execute.side_effect = OSError()
result = self.hardware.get_bios_given_nic_name(interface_name)
self.assertIsNone(result)
mock_execute.assert_called_once_with('biosdevname', '-i',
interface_name)
@mock.patch.object(utils, 'execute', autospec=True)
@mock.patch.object(hardware, 'LOG', autospec=True)
def test_get_bios_given_nic_name_process_exec_err4(self, mock_log,
mock_execute):
interface_name = 'eth0'
mock_execute.side_effect = [
processutils.ProcessExecutionError(exit_code=4)]
result = self.hardware.get_bios_given_nic_name(interface_name)
mock_log.info.assert_called_once_with(
'The system is a virtual machine, so biosdevname utility does '
'not provide names for virtual NICs.')
self.assertIsNone(result)
mock_execute.assert_called_once_with('biosdevname', '-i',
interface_name)
@mock.patch.object(utils, 'execute', autospec=True)
@mock.patch.object(hardware, 'LOG', autospec=True)
def test_get_bios_given_nic_name_process_exec_err3(self, mock_log,
mock_execute):
interface_name = 'eth0'
mock_execute.side_effect = [
processutils.ProcessExecutionError(exit_code=3)]
result = self.hardware.get_bios_given_nic_name(interface_name)
mock_log.warning.assert_called_once_with(
'Biosdevname returned exit code %s', 3)
self.assertIsNone(result)
mock_execute.assert_called_once_with('biosdevname', '-i',
interface_name)
@mock.patch('ironic_python_agent.hardware._get_managers', autospec=True) @mock.patch('ironic_python_agent.hardware._get_managers', autospec=True)
@mock.patch('ironic_python_agent.netutils.get_lldp_info', autospec=True) @mock.patch('ironic_python_agent.netutils.get_lldp_info', autospec=True)
@ -408,7 +497,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
@mock.patch('os.listdir', autospec=True) @mock.patch('os.listdir', autospec=True)
@mock.patch('os.path.exists', autospec=True) @mock.patch('os.path.exists', autospec=True)
@mock.patch('six.moves.builtins.open', autospec=True) @mock.patch('six.moves.builtins.open', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test_list_network_interfaces_with_lldp(self, def test_list_network_interfaces_with_lldp(self,
mocked_execute,
mocked_open, mocked_open,
mocked_exists, mocked_exists,
mocked_listdir, mocked_listdir,
@ -432,6 +523,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
(2, b'\x05Ethernet1/18'), (2, b'\x05Ethernet1/18'),
(3, b'\x00x')] (3, b'\x00x')]
} }
mocked_execute.return_value = ('em0\n', '')
interfaces = self.hardware.list_network_interfaces() interfaces = self.hardware.list_network_interfaces()
self.assertEqual(1, len(interfaces)) self.assertEqual(1, len(interfaces))
self.assertEqual('eth0', interfaces[0].name) self.assertEqual('eth0', interfaces[0].name)
@ -445,6 +537,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
] ]
self.assertEqual(expected_lldp_info, interfaces[0].lldp) self.assertEqual(expected_lldp_info, interfaces[0].lldp)
self.assertTrue(interfaces[0].has_carrier) self.assertTrue(interfaces[0].has_carrier)
self.assertEqual('em0', interfaces[0].biosdevname)
@mock.patch('ironic_python_agent.hardware._get_managers', autospec=True) @mock.patch('ironic_python_agent.hardware._get_managers', autospec=True)
@mock.patch('ironic_python_agent.netutils.get_lldp_info', autospec=True) @mock.patch('ironic_python_agent.netutils.get_lldp_info', autospec=True)
@ -452,8 +545,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
@mock.patch('os.listdir', autospec=True) @mock.patch('os.listdir', autospec=True)
@mock.patch('os.path.exists', autospec=True) @mock.patch('os.path.exists', autospec=True)
@mock.patch('six.moves.builtins.open', autospec=True) @mock.patch('six.moves.builtins.open', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test_list_network_interfaces_with_lldp_error( def test_list_network_interfaces_with_lldp_error(
self, mocked_open, mocked_exists, mocked_listdir, self, mocked_execute, mocked_open, mocked_exists, mocked_listdir,
mocked_ifaddresses, mocked_lldp_info, mocked_get_managers): mocked_ifaddresses, mocked_lldp_info, mocked_get_managers):
mocked_get_managers.return_value = [hardware.GenericHardwareManager()] mocked_get_managers.return_value = [hardware.GenericHardwareManager()]
CONF.set_override('collect_lldp', True) CONF.set_override('collect_lldp', True)
@ -467,6 +561,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
netifaces.AF_INET: [{'addr': '192.168.1.2'}] netifaces.AF_INET: [{'addr': '192.168.1.2'}]
} }
mocked_lldp_info.side_effect = Exception('Boom!') mocked_lldp_info.side_effect = Exception('Boom!')
mocked_execute.return_value = ('em0\n', '')
interfaces = self.hardware.list_network_interfaces() interfaces = self.hardware.list_network_interfaces()
self.assertEqual(1, len(interfaces)) self.assertEqual(1, len(interfaces))
self.assertEqual('eth0', interfaces[0].name) self.assertEqual('eth0', interfaces[0].name)
@ -474,13 +569,16 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('192.168.1.2', interfaces[0].ipv4_address) self.assertEqual('192.168.1.2', interfaces[0].ipv4_address)
self.assertIsNone(interfaces[0].lldp) self.assertIsNone(interfaces[0].lldp)
self.assertTrue(interfaces[0].has_carrier) self.assertTrue(interfaces[0].has_carrier)
self.assertEqual('em0', interfaces[0].biosdevname)
@mock.patch('ironic_python_agent.hardware._get_managers', autospec=True) @mock.patch('ironic_python_agent.hardware._get_managers', autospec=True)
@mock.patch('netifaces.ifaddresses', autospec=True) @mock.patch('netifaces.ifaddresses', autospec=True)
@mock.patch('os.listdir', autospec=True) @mock.patch('os.listdir', autospec=True)
@mock.patch('os.path.exists', autospec=True) @mock.patch('os.path.exists', autospec=True)
@mock.patch('six.moves.builtins.open', autospec=True) @mock.patch('six.moves.builtins.open', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test_list_network_interfaces_no_carrier(self, def test_list_network_interfaces_no_carrier(self,
mocked_execute,
mocked_open, mocked_open,
mocked_exists, mocked_exists,
mocked_listdir, mocked_listdir,
@ -497,6 +595,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mocked_ifaddresses.return_value = { mocked_ifaddresses.return_value = {
netifaces.AF_INET: [{'addr': '192.168.1.2'}] netifaces.AF_INET: [{'addr': '192.168.1.2'}]
} }
mocked_execute.return_value = ('em0\n', '')
interfaces = self.hardware.list_network_interfaces() interfaces = self.hardware.list_network_interfaces()
self.assertEqual(1, len(interfaces)) self.assertEqual(1, len(interfaces))
self.assertEqual('eth0', interfaces[0].name) self.assertEqual('eth0', interfaces[0].name)
@ -504,13 +603,16 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('192.168.1.2', interfaces[0].ipv4_address) self.assertEqual('192.168.1.2', interfaces[0].ipv4_address)
self.assertFalse(interfaces[0].has_carrier) self.assertFalse(interfaces[0].has_carrier)
self.assertIsNone(interfaces[0].vendor) self.assertIsNone(interfaces[0].vendor)
self.assertEqual('em0', interfaces[0].biosdevname)
@mock.patch('ironic_python_agent.hardware._get_managers', autospec=True) @mock.patch('ironic_python_agent.hardware._get_managers', autospec=True)
@mock.patch('netifaces.ifaddresses', autospec=True) @mock.patch('netifaces.ifaddresses', autospec=True)
@mock.patch('os.listdir', autospec=True) @mock.patch('os.listdir', autospec=True)
@mock.patch('os.path.exists', autospec=True) @mock.patch('os.path.exists', autospec=True)
@mock.patch('six.moves.builtins.open', autospec=True) @mock.patch('six.moves.builtins.open', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
def test_list_network_interfaces_with_vendor_info(self, def test_list_network_interfaces_with_vendor_info(self,
mocked_execute,
mocked_open, mocked_open,
mocked_exists, mocked_exists,
mocked_listdir, mocked_listdir,
@ -527,6 +629,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mocked_ifaddresses.return_value = { mocked_ifaddresses.return_value = {
netifaces.AF_INET: [{'addr': '192.168.1.2'}] netifaces.AF_INET: [{'addr': '192.168.1.2'}]
} }
mocked_execute.return_value = ('em0\n', '')
interfaces = self.hardware.list_network_interfaces() interfaces = self.hardware.list_network_interfaces()
self.assertEqual(1, len(interfaces)) self.assertEqual(1, len(interfaces))
self.assertEqual('eth0', interfaces[0].name) self.assertEqual('eth0', interfaces[0].name)
@ -535,6 +638,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertTrue(interfaces[0].has_carrier) self.assertTrue(interfaces[0].has_carrier)
self.assertEqual('0x15b3', interfaces[0].vendor) self.assertEqual('0x15b3', interfaces[0].vendor)
self.assertEqual('0x1014', interfaces[0].product) self.assertEqual('0x1014', interfaces[0].product)
self.assertEqual('em0', interfaces[0].biosdevname)
@mock.patch.object(hardware, 'get_cached_node', autospec=True) @mock.patch.object(hardware, 'get_cached_node', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True) @mock.patch.object(utils, 'execute', autospec=True)

View File

@ -0,0 +1,11 @@
---
features:
- Adds an extra field ``biosdevname`` (BIOS given NICs name) to network
interface inventory collected by ``default`` collector of
ironic-python-agent. Biosdevname utility is used for collecting bios given
NICs name.
issues:
- Collecting the 'biosdevname' field on network interfaces is impossible on any
Debian-based images due to the missing 'biosdevname' utility. This includes
the CoreOS image, as the CoreOS image utilizes a Debian-based chroot.