Skip to content

Commit fdcb092

Browse files
annielezilramay1
authored andcommitted
Collect NIC name given by BIOS
Adds an extra field ``biosdevname`` to network interface inventory collected by ``default`` inspection collector (which collects the whole inventory returned by hardware manager) of ironic-python-agent. This feature requires biosdevname utility to collect the bios given NIC names. The tooling module for tinyIPA is created for the same purpose. For CoreOS IPA pxe images, biosdevname tooling module is limited, because Docker repository is created and embedded into CoreOS pxe images. The Docker repository uses debian to download the packages. Debian does not have biosdevname package. Adds an export variable TINYIPA_REQUIRE_BIOSDEVNAME. Set this variable to ``true`` in your shell before building tinyIPA. Closes-Bug: #1635351 Change-Id: Ia96af59e2a74868cac59e5a88cfbb3be60d85687
1 parent 15878b7 commit fdcb092

File tree

10 files changed

+198
-19
lines changed

10 files changed

+198
-19
lines changed

doc/source/index.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -118,11 +118,11 @@ fields:
118118

119119
``interfaces``
120120
list of network interfaces with fields: ``name``, ``mac_address``,
121-
``ipv4_address``, ``lldp``, ``vendor`` and ``product``.
122-
If configuration option ``collect_lldp`` is set to True the ``lldp``
123-
field will be populated by a list of type-length-value (TLV) fields
124-
retrieved using the Link Layer Discovery Protocol (LLDP).
125-
121+
``ipv4_address``, ``lldp``, ``vendor``, ``product``, and optionally
122+
``biosdevname``(BIOS given NIC name). If configuration option
123+
``collect_lldp`` is set to True the ``lldp`` field will be populated
124+
by a list of type-length-value(TLV) fields retrieved using the
125+
Link Layer Discovery Protocol (LLDP).
126126

127127
``system_vendor``
128128
system vendor information from SMBIOS as reported by ``dmidecode``:

imagebuild/tinyipa/README.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,13 @@ To provide other public SSH key, export path to it in your shell before
105105
building tinyipa as follows::
106106

107107
export SSH_PUBLIC_KEY=<full-path-to-public-key>
108+
109+
110+
Enabling biosdevname in the ramdisk
111+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112+
113+
If you want to collect BIOS given names of NICs in the inventory, set
114+
``TINYIPA_REQUIRE_BIOSDEVNAME`` variable in your shell before building the
115+
tinyipa::
116+
117+
export TINYIPA_REQUIRE_BIOSDEVNAME=true

imagebuild/tinyipa/build-tinyipa.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ source ${WORKDIR}/tc-mirror.sh
66
BUILDDIR="$WORKDIR/tinyipabuild"
77
BUILD_AND_INSTALL_TINYIPA=${BUILD_AND_INSTALL_TINYIPA:-false}
88
TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-}
9+
TINYIPA_REQUIRE_BIOSDEVNAME=${TINYIPA_REQUIRE_BIOSDEVNAME:-false}
910

1011
CHROOT_PATH="/tmp/overides:/usr/local/sbin:/usr/local/bin:/apps/bin:/usr/sbin:/usr/bin:/sbin:/bin"
1112
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"
5758
# Download get-pip into ramdisk
5859
( cd "$BUILDDIR/tmp" && wget https://bootstrap.pypa.io/get-pip.py )
5960

60-
# Download TGT and Qemu-utils source
61+
# Download TGT, Qemu-utils, and Biosdevname source
6162
clone_and_checkout "https://github.com/fujita/tgt.git" "${BUILDDIR}/tmp/tgt" "v1.0.62"
6263
clone_and_checkout "https://github.com/qemu/qemu.git" "${BUILDDIR}/tmp/qemu" "v2.5.0"
64+
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
65+
wget -N -O - https://linux.dell.com/biosdevname/biosdevname-0.7.2/biosdevname-0.7.2.tar.gz | tar -xz -C "${BUILDDIR}/tmp" -f -
66+
fi
6367

6468
# Create directory for python local mirror
6569
mkdir -p "$BUILDDIR/tmp/localpip"
@@ -114,3 +118,11 @@ cd $WORKDIR/build_files && mksquashfs $BUILDDIR/tmp/qemu-utils qemu-utils.tcz &&
114118

115119
# Create qemu-utils.tcz.dep
116120
echo "glib2.tcz" > qemu-utils.tcz.dep
121+
122+
# Build biosdevname
123+
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
124+
rm -rf $WORKDIR/build_files/biosdevname.tcz
125+
$CHROOT_CMD /bin/sh -c "cd /tmp/biosdevname-* && ./configure && make && make install DESTDIR=/tmp/biosdevname-installed"
126+
find $BUILDDIR/tmp/biosdevname-installed/ -type f -executable | xargs file | awk -F ':' '/ELF/ {print $1}' | sudo xargs strip
127+
cd $WORKDIR/build_files && mksquashfs $BUILDDIR/tmp/biosdevname-installed biosdevname.tcz && md5sum biosdevname.tcz > biosdevname.tcz.md5.txt
128+
fi

imagebuild/tinyipa/build_files/buildreqs.lst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ hdparm.tcz
77
parted.tcz
88
python.tcz
99
python-dev.tcz
10+
pciutils.tcz
11+
libpci-dev.tcz
1012
raid-dm-4.2.9-tinycore64.tcz
1113
scsi-4.2.9-tinycore64.tcz
1214
udev-lib.tcz

imagebuild/tinyipa/build_files/finalreqs.lst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ iproute2.tcz
77
parted.tcz
88
popt.tcz
99
python.tcz
10+
pciutils.tcz
1011
raid-dm-4.2.9-tinycore64.tcz
1112
scsi-4.2.9-tinycore64.tcz
1213
udev-lib.tcz

imagebuild/tinyipa/finalise-tinyipa.sh

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ TINYCORE_MIRROR_URL=${TINYCORE_MIRROR_URL:-}
1010
ENABLE_SSH=${ENABLE_SSH:-false}
1111
SSH_PUBLIC_KEY=${SSH_PUBLIC_KEY:-}
1212
PYOPTIMIZE_TINYIPA=${PYOPTIMIZE_TINYIPA:-true}
13+
TINYIPA_REQUIRE_BIOSDEVNAME=${TINYIPA_REQUIRE_BIOSDEVNAME:-false}
1314

1415
TC=1001
1516
STAFF=50
@@ -86,6 +87,9 @@ echo "tc" | $CHROOT_CMD tee -a /etc/sysconfig/tcuser
8687

8788
cp $WORKDIR/build_files/tgt.* $FINALDIR/tmp/builtin/optional
8889
cp $WORKDIR/build_files/qemu-utils.* $FINALDIR/tmp/builtin/optional
90+
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
91+
cp $WORKDIR/build_files/biosdevname.* $FINALDIR/tmp/builtin/optional
92+
fi
8993

9094
# Mount /proc for chroot commands
9195
sudo mount --bind /proc $FINALDIR/proc
@@ -123,6 +127,9 @@ fi
123127

124128
$TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/tgt.tcz
125129
$TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/qemu-utils.tcz
130+
if $TINYIPA_REQUIRE_BIOSDEVNAME; then
131+
$TC_CHROOT_CMD tce-load -ic /tmp/builtin/optional/biosdevname.tcz
132+
fi
126133

127134
# Ensure tinyipa picks up installed kernel modules
128135
$CHROOT_CMD depmod -a `$WORKDIR/build_files/fakeuname -r`

ironic_python_agent/hardware.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -218,17 +218,19 @@ def __init__(self, name, model, size, rotational, wwn=None, serial=None,
218218
class NetworkInterface(encoding.SerializableComparable):
219219
serializable_fields = ('name', 'mac_address', 'ipv4_address',
220220
'has_carrier', 'lldp', 'vendor', 'product',
221-
'client_id')
221+
'client_id', 'biosdevname')
222222

223223
def __init__(self, name, mac_addr, ipv4_address=None, has_carrier=True,
224-
lldp=None, vendor=None, product=None, client_id=None):
224+
lldp=None, vendor=None, product=None, client_id=None,
225+
biosdevname=None):
225226
self.name = name
226227
self.mac_address = mac_addr
227228
self.ipv4_address = ipv4_address
228229
self.has_carrier = has_carrier
229230
self.lldp = lldp
230231
self.vendor = vendor
231232
self.product = product
233+
self.biosdevname = biosdevname
232234
# client_id is used for InfiniBand only. we calculate the DHCP
233235
# client identifier Option to allow DHCP to work over InfiniBand.
234236
# see https://tools.ietf.org/html/rfc4390
@@ -531,11 +533,41 @@ def get_interface_info(self, interface_name):
531533
ipv4_address=self.get_ipv4_addr(interface_name),
532534
has_carrier=netutils.interface_has_carrier(interface_name),
533535
vendor=_get_device_info(interface_name, 'net', 'vendor'),
534-
product=_get_device_info(interface_name, 'net', 'device'))
536+
product=_get_device_info(interface_name, 'net', 'device'),
537+
biosdevname=self.get_bios_given_nic_name(interface_name))
535538

536539
def get_ipv4_addr(self, interface_id):
537540
return netutils.get_ipv4_addr(interface_id)
538541

542+
def get_bios_given_nic_name(self, interface_name):
543+
"""Collect the BIOS given NICs name.
544+
545+
This function uses the biosdevname utility to collect the BIOS given
546+
name of network interfaces.
547+
548+
The collected data is added to the network interface inventory with an
549+
extra field named ``biosdevname``.
550+
551+
:param interface_name: list of names of node's interfaces.
552+
:return: the BIOS given NIC name of node's interfaces or default
553+
as None.
554+
"""
555+
try:
556+
stdout, _ = utils.execute('biosdevname', '-i',
557+
interface_name)
558+
return stdout.rstrip('\n')
559+
except OSError:
560+
LOG.warning("Executable 'biosdevname' not found")
561+
return
562+
except processutils.ProcessExecutionError as e:
563+
# NOTE(alezil) biosdevname returns 4 if running in a
564+
# virtual machine.
565+
if e.exit_code == 4:
566+
LOG.info('The system is a virtual machine, so biosdevname '
567+
'utility does not provide names for virtual NICs.')
568+
else:
569+
LOG.warning('Biosdevname returned exit code %s', e.exit_code)
570+
539571
def _is_device(self, interface_name):
540572
device_path = '{}/class/net/{}/device'.format(self.sys_path,
541573
interface_name)

ironic_python_agent/tests/unit/test_agent.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -385,15 +385,14 @@ def test__wait_for_interface_expired(self, mock_dispatch, mock_sleep,
385385
mock_dispatch.assert_has_calls(expected_dispatch_calls)
386386
mock_sleep.assert_has_calls(expected_sleep_calls)
387387

388-
@mock.patch('ironic_python_agent.hardware_managers.cna._detect_cna_card',
389-
autospec=True)
388+
@mock.patch.object(hardware, 'load_managers', autospec=True)
390389
@mock.patch.object(time, 'sleep', autospec=True)
391-
@mock.patch('wsgiref.simple_server.make_server', autospec=True)
392-
@mock.patch.object(hardware, '_check_for_iscsi', autospec=True)
393-
@mock.patch.object(hardware.HardwareManager, 'list_hardware_info',
390+
@mock.patch.object(agent.IronicPythonAgent, '_wait_for_interface',
394391
autospec=True)
395-
def test_run_with_sleep(self, mock_check_for_iscsi, mock_list_hardware,
396-
mock_make_server, mock_sleep, mock_cna):
392+
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
393+
@mock.patch('wsgiref.simple_server.make_server', autospec=True)
394+
def test_run_with_sleep(self, mock_make_server, mock_dispatch,
395+
mock_load_managers, mock_sleep, mock_wait):
397396
CONF.set_override('inspection_callback_url', '', enforce_type=True)
398397
wsgi_server = mock_make_server.return_value
399398
wsgi_server.start.side_effect = KeyboardInterrupt()
@@ -409,7 +408,6 @@ def test_run_with_sleep(self, mock_check_for_iscsi, mock_list_hardware,
409408
'heartbeat_timeout': 300
410409
}
411410
}
412-
mock_cna.return_value = False
413411
self.agent.run()
414412

415413
listen_addr = agent.Host('192.0.2.1', 9999)
@@ -422,7 +420,9 @@ def test_run_with_sleep(self, mock_check_for_iscsi, mock_list_hardware,
422420

423421
self.agent.heartbeater.start.assert_called_once_with()
424422
mock_sleep.assert_called_once_with(10)
425-
self.assertTrue(mock_check_for_iscsi.called)
423+
self.assertTrue(mock_load_managers.called)
424+
self.assertTrue(mock_wait.called)
425+
mock_dispatch.assert_called_once_with('list_hardware_info')
426426

427427
def test_async_command_success(self):
428428
result = base.AsyncCommandResult('foo_command', {'fail': False},

0 commit comments

Comments
 (0)