Merge "Add new introspection commands for interface data including lldp"

This commit is contained in:
Jenkins 2017-02-15 10:49:27 +00:00 committed by Gerrit Code Review
commit 79a282a397
9 changed files with 698 additions and 1 deletions

View File

@ -160,3 +160,34 @@ calls.
.. _introspection rules documentation: http://docs.openstack.org/developer/ironic-inspector/usage.html#introspection-rules
List interface data
~~~~~~~~~~~~~~~~~~~
::
$ openstack baremetal introspection interface list NODE_IDENT
[--fields=<field>] [--vlan=<vlan>]
* ``NODE_IDENT`` - Ironic node UUID or name
* ``fields`` - name of one or more interface columns to display.
* ``vlan`` - list only interfaces configured for this vlan id
Returns a list of interface data, including attached switch information,
for each interface on the node.
Show interface data
~~~~~~~~~~~~~~~~~~~
::
$ openstack baremetal introspection interface show NODE_IDENT INTERFACE
[--fields=<field>]
* ``NODE_IDENT`` - Ironic node UUID or name
* ``INTERFACE`` - interface name on this node
* ``fields`` - name of one or more interface rows to display.
Show interface data, including attached switch information,
for a particular node and interface.

View File

@ -0,0 +1,93 @@
# 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.
class InterfaceResource(object):
"""InterfaceResource class
This class is used to manage the fields including Link Layer Discovery
Protocols (LLDP) fields, that an interface contains. An individual
field consists of a 'field_id' (key) and a 'label' (value).
"""
FIELDS = {
'interface': 'Interface',
'mac': 'MAC Address',
'node_ident': 'Node',
'switch_capabilities_enabled': 'Switch Capabilities Enabled',
'switch_capabilities_support': 'Switch Capabilities Supported',
'switch_chassis_id': 'Switch Chassis ID',
'switch_port_autonegotiation_enabled':
'Switch Port Autonegotiation Enabled',
'switch_port_autonegotiation_support':
'Switch Port Autonegotiation Supported',
'switch_port_description': 'Switch Port Description',
'switch_port_id': 'Switch Port ID',
'switch_port_link_aggregation_enabled':
'Switch Port Link Aggregation Enabled',
'switch_port_link_aggregation_support':
'Switch Port Link Aggregation Supported',
'switch_port_link_aggregation_id': 'Switch Port Link Aggregation ID',
'switch_port_management_vlan_id': 'Switch Port Mgmt VLAN ID',
'switch_port_mau_type': 'Switch Port Mau Type',
'switch_port_mtu': 'Switch Port MTU',
'switch_port_physical_capabilities':
'Switch Port Physical Capabilities',
'switch_port_protocol_vlan_enabled':
'Switch Port Protocol VLAN Enabled',
'switch_port_protocol_vlan_support':
'Switch Port Protocol VLAN Supported',
'switch_port_protocol_vlan_ids': 'Switch Port Protocol VLAN IDs',
'switch_port_untagged_vlan_id': 'Switch Port Untagged VLAN',
'switch_port_vlans': 'Switch Port VLANs',
'switch_port_vlan_ids': 'Switch Port VLAN IDs',
'switch_protocol_identities': 'Switch Protocol Identities',
'switch_system_name': 'Switch System Name'
}
DEFAULT_FIELD_IDS = ['interface',
'mac',
'switch_port_vlan_ids',
'switch_chassis_id',
'switch_port_id']
def __init__(self, field_ids=None, detailed=False):
"""Create an InterfaceResource object
:param field_ids: A list of strings that the Resource object will
contain. Each string must match an existing key in
FIELDS.
:param detailed: If True, use the all of the keys in FIELDS instead
of input field_ids
"""
if field_ids is None:
# Default field set in logical format, so don't sort
field_ids = self.DEFAULT_FIELD_IDS
if detailed:
field_ids = sorted(self.FIELDS.keys())
self._fields = tuple(field_ids)
self._labels = tuple(self.FIELDS[x] for x in field_ids)
@property
def fields(self):
return self._fields
@property
def labels(self):
return self._labels
INTERFACE_DEFAULT = InterfaceResource()

View File

@ -22,7 +22,7 @@ from osc_lib.command import command
from osc_lib import utils
import ironic_inspector_client
from ironic_inspector_client import resource as res
API_NAME = 'baremetal_introspection'
API_VERSION_OPTION = 'inspector_api_version'
@ -282,3 +282,77 @@ class DataSaveCommand(command.Command):
fp.write(data)
else:
json.dump(data, sys.stdout)
class InterfaceListCommand(command.Lister):
"""List interface data including attached switch port information."""
def get_parser(self, prog_name):
parser = super(InterfaceListCommand, self).get_parser(prog_name)
parser.add_argument('node_ident', help='baremetal node UUID or name')
parser.add_argument("--vlan",
action='append',
default=[], type=int,
help="List only interfaces configured "
"for this vlan id, can be repeated")
display_group = parser.add_mutually_exclusive_group()
display_group.add_argument(
'--long', dest='detail',
action='store_true', default=False,
help="Show detailed information about interfaces.")
display_group.add_argument(
'--fields', nargs='+', dest='fields',
metavar='<field>',
choices=sorted(res.InterfaceResource(detailed=True).fields),
help="Display one or more fields. "
"Can not be used when '--long' is specified")
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.baremetal_introspection
# If --long defined, use all fields
interface_res = res.InterfaceResource(parsed_args.fields,
parsed_args.detail)
rows = client.get_all_interface_data(parsed_args.node_ident,
interface_res.fields,
vlan=parsed_args.vlan)
return interface_res.labels, rows
class InterfaceShowCommand(command.ShowOne):
"""Show interface data including attached switch port information."""
COLUMNS = ("Field", "Value")
def get_parser(self, prog_name):
parser = super(InterfaceShowCommand, self).get_parser(prog_name)
parser.add_argument('node_ident', help='baremetal node UUID or name')
parser.add_argument('interface', help='interface name')
parser.add_argument(
'--fields', nargs='+', dest='fields',
metavar='<field>',
choices=sorted(res.InterfaceResource(detailed=True).fields),
help="Display one or more fields.")
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.baremetal_introspection
if parsed_args.fields:
interface_res = res.InterfaceResource(parsed_args.fields)
else:
# Show all fields in detailed resource
interface_res = res.InterfaceResource(detailed=True)
iface_dict = client.get_interface_data(parsed_args.node_ident,
parsed_args.interface,
interface_res.fields)
return tuple(zip(*(iface_dict.items())))

View File

@ -14,6 +14,7 @@
import eventlet
eventlet.monkey_patch()
import copy
import json
import mock
import os
@ -331,6 +332,34 @@ class BaseCLITest(functional.Base):
class TestCLI(BaseCLITest):
def setup_lldp(self):
functional.cfg.CONF.set_override('store_data', 'swift', 'processing')
self.all_interfaces = {
'eth1': {'mac': self.macs[0], 'ip': self.ips[0],
'client_id': None, 'lldp_processed':
{'switch_chassis_id': "11:22:33:aa:bb:cc",
'switch_port_vlans':
[{"name": "vlan101", "id": 101},
{"name": "vlan102", "id": 102},
{"name": "vlan104", "id": 104},
{"name": "vlan201", "id": 201},
{"name": "vlan203", "id": 203}],
'switch_port_id': "554",
'switch_port_mtu': 1514}},
'eth3': {'mac': self.macs[1], 'ip': None,
'client_id': None, 'lldp_processed':
{'switch_chassis_id': "11:22:33:aa:bb:cc",
'switch_port_vlans':
[{"name": "vlan101", "id": 101},
{"name": "vlan102", "id": 102},
{"name": "vlan104", "id": 106}],
'switch_port_id': "557",
'switch_port_mtu': 9216}}
}
self.data['all_interfaces'] = self.all_interfaces
def _fake_status(self, **kwargs):
# to remove the hidden fields
hidden_status_items = shell.StatusCommand.hidden_status_items
@ -352,6 +381,9 @@ class TestCLI(BaseCLITest):
err = self.run_cli('rule', 'delete', 'uuid', expect_error=True)
self.assertIn('not found', err)
err = self.run_cli('interface', 'list', expect_error=True)
self.assertIn('too few arguments', err)
def test_introspect_get_status(self):
self.run_cli('start', self.uuid)
eventlet.greenthread.sleep(functional.DEFAULT_SLEEP)
@ -410,6 +442,62 @@ class TestCLI(BaseCLITest):
res = self.run_cli('rule', 'list', parse_json=True)
self.assertEqual([], res)
@mock.patch.object(swift, 'get_introspection_data', autospec=True)
def test_interface_list(self, get_mock):
self.setup_lldp()
get_mock.return_value = json.dumps(copy.deepcopy(self.data))
expected_eth1 = {u'Interface': u'eth1',
u'MAC Address': u'11:22:33:44:55:66',
u'Switch Chassis ID': u'11:22:33:aa:bb:cc',
u'Switch Port ID': u'554',
u'Switch Port VLAN IDs': [101, 102, 104, 201, 203]}
expected_eth3 = {u'Interface': u'eth3',
u'MAC Address': u'66:55:44:33:22:11',
u'Switch Chassis ID': u'11:22:33:aa:bb:cc',
u'Switch Port ID': u'557',
u'Switch Port VLAN IDs': [101, 102, 106]}
res = self.run_cli('interface', 'list', self.uuid, parse_json=True)
res.sort()
self.assertEqual(expected_eth1, res[0])
self.assertEqual(expected_eth3, res[1])
# Filter on vlan
res = self.run_cli('interface', 'list', self.uuid, '--vlan', '106',
parse_json=True)
res.sort()
self.assertEqual(expected_eth3, res[0])
# Select fields
res = self.run_cli('interface', 'list', self.uuid, '--fields',
'switch_port_mtu',
parse_json=True)
res.sort()
self.assertEqual({u'Switch Port MTU': 1514}, res[0])
self.assertEqual({u'Switch Port MTU': 9216}, res[1])
@mock.patch.object(swift, 'get_introspection_data', autospec=True)
def test_interface_show(self, get_mock):
self.setup_lldp()
get_mock.return_value = json.dumps(copy.deepcopy(self.data))
res = self.run_cli('interface', 'show', self.uuid, "eth1",
parse_json=True)
expected = {u'interface': u'eth1',
u'mac': u'11:22:33:44:55:66',
u'switch_chassis_id': u'11:22:33:aa:bb:cc',
u'switch_port_id': u'554',
u'switch_port_mtu': 1514,
u'switch_port_vlan_ids': [101, 102, 104, 201, 203],
u'switch_port_vlans': [{u'id': 101, u'name': u'vlan101'},
{u'id': 102, u'name': u'vlan102'},
{u'id': 104, u'name': u'vlan104'},
{u'id': 201, u'name': u'vlan201'},
{u'id': 203, u'name': u'vlan203'}]}
self.assertDictContainsSubset(expected, res)
if __name__ == '__main__':
with functional.mocked_server():

View File

@ -13,6 +13,7 @@
import sys
from collections import OrderedDict
import mock
from osc_lib.tests import utils
import six
@ -351,3 +352,204 @@ class TestDataSave(BaseTest):
self.assertEqual(b'{"answer": 42}', content)
self.client.get_data.assert_called_once_with('uuid1', raw=True)
class TestInterfaceCmds(BaseTest):
def setUp(self):
super(TestInterfaceCmds, self).setUp()
self.inspector_db = {
"all_interfaces":
{
'em1': {'mac': "00:11:22:33:44:55", 'ip': "10.10.1.1",
"lldp_processed": {
"switch_chassis_id": "99:aa:bb:cc:dd:ff",
"switch_port_id": "555",
"switch_port_vlans":
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}],
"switch_port_mtu": 1514
}
}
}
}
def test_list(self):
self.client.get_all_interface_data.return_value = [
["em1", "00:11:22:33:44:55", [101, 102, 104, 201, 203],
"99:aa:bb:cc:dd:ff", "555"],
["em2", "00:11:22:66:77:88", [201, 203],
"99:aa:bb:cc:dd:ff", "777"],
["em3", "00:11:22:aa:bb:cc", '', '', '']]
arglist = ['uuid1']
verifylist = [('node_ident', 'uuid1')]
cmd = shell.InterfaceListCommand(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
cols, values = cmd.take_action(parsed_args)
expected_cols = ("Interface", "MAC Address", "Switch Port VLAN IDs",
"Switch Chassis ID", "Switch Port ID")
# Note that em3 has no lldp data
expected_rows = [["em1", "00:11:22:33:44:55",
[101, 102, 104, 201, 203],
"99:aa:bb:cc:dd:ff",
"555"],
["em2", "00:11:22:66:77:88",
[201, 203],
"99:aa:bb:cc:dd:ff",
"777"],
["em3", "00:11:22:aa:bb:cc", '', '', '']]
self.assertEqual(expected_cols, cols)
self.assertEqual(expected_rows, values)
def test_list_field(self):
self.client.get_all_interface_data.return_value = [
["em1", 1514],
["em2", 9216],
["em3", '']]
arglist = ['uuid1', '--fields', 'interface',
"switch_port_mtu"]
verifylist = [('node_ident', 'uuid1'),
('fields', ["interface", "switch_port_mtu"])]
cmd = shell.InterfaceListCommand(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
cols, values = cmd.take_action(parsed_args)
expected_cols = ("Interface", "Switch Port MTU")
expected_rows = [["em1", 1514],
["em2", 9216],
["em3", '']]
self.assertEqual(expected_cols, cols)
self.assertEqual(expected_rows, values)
def test_list_filtered(self):
self.client.get_all_interface_data.return_value = [
["em1",
"00:11:22:33:44:55",
[101, 102, 104, 201, 203],
"99:aa:bb:cc:dd:ff",
"555"]]
arglist = ['uuid1', '--vlan', '104']
verifylist = [('node_ident', 'uuid1'),
('vlan', [104])]
cmd = shell.InterfaceListCommand(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
cols, values = cmd.take_action(parsed_args)
expected_cols = ("Interface", "MAC Address", "Switch Port VLAN IDs",
"Switch Chassis ID", "Switch Port ID")
expected_rows = [["em1", "00:11:22:33:44:55",
[101, 102, 104, 201, 203],
"99:aa:bb:cc:dd:ff",
"555"]]
self.assertEqual(expected_cols, cols)
self.assertEqual(expected_rows, values)
def test_list_no_data(self):
self.client.get_all_interface_data.return_value = [[]]
arglist = ['uuid1']
verifylist = [('node_ident', 'uuid1')]
cmd = shell.InterfaceListCommand(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
cols, values = cmd.take_action(parsed_args)
expected_cols = ("Interface", "MAC Address", "Switch Port VLAN IDs",
"Switch Chassis ID", "Switch Port ID")
expected_rows = [[]]
self.assertEqual(expected_cols, cols)
self.assertEqual(expected_rows, values)
def test_show(self):
self.client.get_data.return_value = self.inspector_db
data = OrderedDict(
[('node_ident', "uuid1"),
('interface', "em1"),
('mac', "00:11:22:33:44:55"),
('switch_chassis_id', "99:aa:bb:cc:dd:ff"),
('switch_port_id', "555"),
('switch_port_mtu', 1514),
('switch_port_vlans',
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}])]
)
self.client.get_interface_data.return_value = data
arglist = ['uuid1', 'em1']
verifylist = [('node_ident', 'uuid1'), ('interface', 'em1')]
cmd = shell.InterfaceShowCommand(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
cols, values = cmd.take_action(parsed_args)
expected_cols = ("node_ident", "interface", "mac",
"switch_chassis_id", "switch_port_id",
"switch_port_mtu", "switch_port_vlans")
expected_rows = ("uuid1", "em1", "00:11:22:33:44:55",
"99:aa:bb:cc:dd:ff", "555", 1514,
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}])
self.assertEqual(expected_cols, cols)
self.assertEqual(expected_rows, values)
def test_show_field(self):
self.client.get_data.return_value = self.inspector_db
data = OrderedDict([('node_ident', "uuid1"),
('interface', "em1"),
('switch_port_vlans',
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}])
])
self.client.get_interface_data.return_value = data
arglist = ['uuid1', 'em1', '--fields', 'node_ident', 'interface',
"switch_port_vlans"]
verifylist = [('node_ident', 'uuid1'), ('interface', 'em1'),
('fields', ["node_ident", "interface",
"switch_port_vlans"])]
cmd = shell.InterfaceShowCommand(self.app, None)
parsed_args = self.check_parser(cmd, arglist, verifylist)
cols, values = cmd.take_action(parsed_args)
expected_cols = ("node_ident", "interface", "switch_port_vlans")
expected_rows = ("uuid1", "em1",
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}])
self.assertEqual(expected_cols, cols)
self.assertEqual(expected_rows, values)

View File

@ -11,6 +11,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from collections import OrderedDict
import six
import unittest
@ -325,3 +326,122 @@ class TestAbort(BaseTest):
def test_invalid_input(self, _):
self.assertRaises(TypeError, self.get_client().abort, 42)
@mock.patch.object(http.BaseClient, 'request')
class TestInterfaceApi(BaseTest):
def setUp(self):
super(TestInterfaceApi, self).setUp()
self.inspector_db = {
"all_interfaces": {
'em1': {'mac': "00:11:22:33:44:55", 'ip': "10.10.1.1",
"lldp_processed": {
"switch_chassis_id": "99:aa:bb:cc:dd:ff",
"switch_port_id": "555",
"switch_port_vlans":
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}],
"switch_port_mtu": 1514}
},
'em2': {'mac': "00:11:22:66:77:88", 'ip': "10.10.1.2",
"lldp_processed": {
"switch_chassis_id": "99:aa:bb:cc:dd:ff",
"switch_port_id": "777",
"switch_port_vlans":
[{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}],
"switch_port_mtu": 9216}
},
'em3': {'mac': "00:11:22:aa:bb:cc", 'ip': "10.10.1.2"}
}
}
def test_all_interfaces(self, mock_req):
mock_req.return_value.json.return_value = self.inspector_db
fields = ['interface', 'mac', 'switch_chassis_id', 'switch_port_id',
'switch_port_vlans']
expected = [['em1', '00:11:22:33:44:55', '99:aa:bb:cc:dd:ff', '555',
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}]],
['em2', '00:11:22:66:77:88', '99:aa:bb:cc:dd:ff', '777',
[{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}]],
['em3', '00:11:22:aa:bb:cc', None, None, None]]
actual = self.get_client().get_all_interface_data(self.uuid,
fields)
self.assertEqual(sorted(expected), sorted(actual))
# Change fields
fields = ['interface', 'switch_port_mtu']
expected = [
['em1', 1514],
['em2', 9216],
['em3', None]]
actual = self.get_client().get_all_interface_data(self.uuid, fields)
self.assertEqual(expected, sorted(actual))
def test_all_interfaces_filtered(self, mock_req):
mock_req.return_value.json.return_value = self.inspector_db
fields = ['interface', 'mac', 'switch_chassis_id', 'switch_port_id',
'switch_port_vlan_ids']
expected = [['em1', '00:11:22:33:44:55', '99:aa:bb:cc:dd:ff', '555',
[101, 102, 104, 201, 203]]]
# Filter on expected VLAN
vlan = [104]
actual = self.get_client().get_all_interface_data(self.uuid,
fields, vlan=vlan)
self.assertEqual(expected, actual)
# VLANs don't match existing vlans
vlan = [111, 555]
actual = self.get_client().get_all_interface_data(self.uuid,
fields, vlan=vlan)
self.assertEqual([], actual)
def test_one_interface(self, mock_req):
mock_req.return_value.json.return_value = self.inspector_db
# Note that a value for 'switch_foo' will not be found
fields = ["node_ident", "interface", "mac", "switch_port_vlan_ids",
"switch_chassis_id", "switch_port_id",
"switch_port_mtu", "switch_port_vlans", "switch_foo"]
expected_values = OrderedDict(
[('node_ident', self.uuid),
('interface', "em1"),
('mac', "00:11:22:33:44:55"),
('switch_port_vlan_ids',
[101, 102, 104, 201, 203]),
('switch_chassis_id', "99:aa:bb:cc:dd:ff"),
('switch_port_id', "555"),
('switch_port_mtu', 1514),
('switch_port_vlans',
[{"id": 101, "name": "vlan101"},
{"id": 102, "name": "vlan102"},
{"id": 104, "name": "vlan104"},
{"id": 201, "name": "vlan201"},
{"id": 203, "name": "vlan203"}]),
("switch_foo", None)])
iface_dict = self.get_client().get_interface_data(
self.uuid, "em1", fields)
self.assertEqual(expected_values, iface_dict)
# Test interface name not in 'all_interfaces'
expected_values = OrderedDict()
iface_dict = self.get_client().get_interface_data(
self.uuid, "em55", fields)
self.assertEqual(expected_values, iface_dict)

View File

@ -13,6 +13,7 @@
"""Client for V1 API."""
from collections import OrderedDict
import logging
import time
@ -274,6 +275,84 @@ class ClientV1(http.BaseClient):
return self.request('post', '/introspection/%s/abort' % uuid)
def get_interface_data(self, node_ident, interface, field_sel):
"""Get interface data for the input node and interface
To get LLDP data, collection must be enabled by the kernel parameter
ipa-collect-lldp=1, and the inspector plugin ``basic_lldp`` must
be enabled.
:param node_ident: node UUID or name
:param interface: interface name
:param field_sel: list of all fields for which to get data
:returns interface data in OrderedDict
"""
# Use OrderedDict to maintain order of user-entered fields
iface_data = OrderedDict()
data = self.get_data(node_ident)
all_interfaces = data.get('all_interfaces', [])
# Make sure interface name is valid
if interface not in all_interfaces:
return iface_data
# If lldp data not available this will still return interface,
# mac, node_ident etc.
lldp_proc = all_interfaces[interface].get('lldp_processed', {})
for f in field_sel:
if f == 'node_ident':
iface_data[f] = node_ident
elif f == 'interface':
iface_data[f] = interface
elif f == 'mac':
iface_data[f] = all_interfaces[interface].get(f)
elif f == 'switch_port_vlan_ids':
iface_data[f] = [item['id'] for item in
lldp_proc.get('switch_port_vlans', [])]
else:
iface_data[f] = lldp_proc.get(f)
return iface_data
def get_all_interface_data(self, node_ident,
field_sel, vlan=None):
"""Get interface data for all of the interfaces on this node
:param node_ident: node UUID or name
:param field_sel: list of all fields for which to get data
:param vlan: list of vlans used to filter the lists returned
:returns list of interface data, each interface in a list
"""
# Get inventory data for this node
data = self.get_data(node_ident)
all_interfaces = data.get('all_interfaces', [])
rows = []
if vlan:
vlan = set(vlan)
# walk all interfaces, appending data to row if not filtered
for interface in all_interfaces:
iface_dict = self.get_interface_data(node_ident,
interface,
field_sel)
values = list(iface_dict.values())
# Use (optional) vlans to filter row
if not vlan:
rows.append(values)
continue
# curr_vlans may be None
curr_vlans = iface_dict.get('switch_port_vlan_ids', [])
if curr_vlans and (vlan & set(curr_vlans)):
rows.append(values) # vlan matches, display this row
return rows
class RulesAPI(object):
"""Introspection rules API.

View File

@ -0,0 +1,8 @@
---
features:
- Add ``introspection interface list`` and ``introspection interface show``
commands to display stored introspection data for the node including
attached switch port information. In order to get switch data,
LLDP data collection must be enabled by the kernel parameter
``ipa-collect-lldp=1`` and the inspector plugin ``basic_lldp`` must
be enabled.

View File

@ -34,6 +34,8 @@ openstack.baremetal_introspection.v1 =
baremetal_introspection_rule_show = ironic_inspector_client.shell:RuleShowCommand
baremetal_introspection_rule_delete = ironic_inspector_client.shell:RuleDeleteCommand
baremetal_introspection_rule_purge = ironic_inspector_client.shell:RulePurgeCommand
baremetal_introspection_interface_list = ironic_inspector_client.shell:InterfaceListCommand
baremetal_introspection_interface_show = ironic_inspector_client.shell:InterfaceShowCommand
[pbr]
autodoc_index_modules = True