Enable OVS LLDP inventory

This commit introduces a pluggable lldp driver mechanism to enable LLDP
discovery and reporting.

An lldpd driver and OVS extension driver are included.  By default, the
lldpd driver is enabled, which operates over dedicated ports created
in OVS, as well as standard linux interfaces.

The OVS extension ensures that LLDP flows are present on periodic
agent / neighbour polling by the sysinv agent.

To add new drivers, a user would modify the [lldp] drivers value in
sysinv.conf and add to the driver namespace (sysinv.agent.lldp.driver)

Story: 2002946
Task: 22941

Signed-off-by: Steven Webster <steven.webster@windriver.com>
Change-Id: I9b7024b39ca93f46a8814e60d540f234e7f8eef2
This commit is contained in:
Steven Webster 2018-09-19 20:45:42 -04:00
parent da1110a3d8
commit b0c871d0fd
16 changed files with 999 additions and 673 deletions

View File

@ -1,2 +1,2 @@
SRC_DIR="sysinv"
TIS_PATCH_VER=281
TIS_PATCH_VER=282

View File

@ -17,3 +17,6 @@ connection=postgresql://cgts:cgtspwd@localhost/cgtsdb:
rpc_backend = sysinv.openstack.common.rpc.impl_kombu
rabbit_host = 192.168.204.3
rabbit_port = 5672
[lldp]
drivers=lldpd

View File

@ -72,6 +72,10 @@ systemconfig.puppet_plugins =
032_swift = sysinv.puppet.swift:SwiftPuppet
033_service_parameter = sysinv.puppet.service_parameter:ServiceParamPuppet
sysinv.agent.lldp.drivers =
lldpd = sysinv.agent.lldp.drivers.lldpd.driver:SysinvLldpdAgentDriver
ovs = sysinv.agent.lldp.drivers.ovs.driver:SysinvOVSAgentDriver
[pbr]
autodoc_index_modules = True

View File

@ -1,661 +0,0 @@
#
# Copyright (c) 2016 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
""" inventory lldp Utilities and helper functions."""
import simplejson as json
import subprocess
import threading
from operator import attrgetter
from sysinv.common import constants
from sysinv.openstack.common import log as logging
LOG = logging.getLogger(__name__)
class Key(object):
def __init__(self, chassisid, portid, portname):
self.chassisid = chassisid
self.portid = portid
self.portname = portname
def __hash__(self):
return hash((self.chassisid, self.portid, self.portname))
def __cmp__(self, rhs):
return (cmp(self.chassisid, rhs.chassisid) or
cmp(self.portid, rhs.portid) or
cmp(self.portname, rhs.portname))
def __eq__(self, rhs):
return (self.chassisid == rhs.chassisid and
self.portid == rhs.portid and
self.portname == rhs.portname)
def __ne__(self, rhs):
return (self.chassisid != rhs.chassisid or
self.portid != rhs.portid or
self.portname != rhs.portname)
def __str__(self):
return "%s [%s] [%s]" % (self.portname, self.chassisid, self.portid)
def __repr__(self):
return "<Key '%s'>" % str(self)
class Agent(object):
'''Class to encapsulate LLDP agent data for System Inventory'''
def __init__(self, **kwargs):
'''Construct an Agent object with the given values.'''
self.key = Key(kwargs.get(constants.LLDP_TLV_TYPE_CHASSIS_ID),
kwargs.get(constants.LLDP_TLV_TYPE_PORT_ID),
kwargs.get("name_or_uuid"))
self.status = kwargs.get('status')
self.ttl = kwargs.get(constants.LLDP_TLV_TYPE_TTL)
self.system_name = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_NAME)
self.system_desc = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_DESC)
self.port_desc = kwargs.get(constants.LLDP_TLV_TYPE_PORT_DESC)
self.capabilities = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_CAP)
self.mgmt_addr = kwargs.get(constants.LLDP_TLV_TYPE_MGMT_ADDR)
self.dot1_lag = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_LAG)
self.dot1_vlan_names = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES)
self.dot3_max_frame = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME)
self.state = None
def __hash__(self):
return self.key.__hash__()
def __eq__(self, rhs):
return (self.key == rhs.key)
def __ne__(self, rhs):
return (self.key != rhs.key or
self.status != rhs.status or
self.ttl != rhs.ttl or
self.system_name != rhs.system_name or
self.system_desc != rhs.system_desc or
self.port_desc != rhs.port_desc or
self.capabilities != rhs.capabilities or
self.mgmt_addr != rhs.mgmt_addr or
self.dot1_lag != rhs.dot1_lag or
self.dot1_vlan_names != rhs.dot1_vlan_names or
self.dot3_max_frame != rhs.dot3_max_frame or
self.state != rhs.state)
def __str__(self):
return "%s: [%s] [%s] [%s], [%s], [%s], [%s], [%s], [%s]" % (
self.key, self.status, self.system_name, self.system_desc,
self.port_desc, self.capabilities,
self.mgmt_addr, self.dot1_lag,
self.dot3_max_frame)
def __repr__(self):
return "<Agent '%s'>" % str(self)
class Neighbour(object):
'''Class to encapsulate LLDP neighbour data for System Inventory'''
def __init__(self, **kwargs):
'''Construct an Neighbour object with the given values.'''
self.key = Key(kwargs.get(constants.LLDP_TLV_TYPE_CHASSIS_ID),
kwargs.get(constants.LLDP_TLV_TYPE_PORT_ID),
kwargs.get("name_or_uuid"))
self.msap = kwargs.get('msap')
self.ttl = kwargs.get(constants.LLDP_TLV_TYPE_TTL)
self.system_name = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_NAME)
self.system_desc = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_DESC)
self.port_desc = kwargs.get(constants.LLDP_TLV_TYPE_PORT_DESC)
self.capabilities = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_CAP)
self.mgmt_addr = kwargs.get(constants.LLDP_TLV_TYPE_MGMT_ADDR)
self.dot1_port_vid = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_PORT_VID)
self.dot1_vid_digest = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST)
self.dot1_mgmt_vid = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_MGMT_VID)
self.dot1_vid_digest = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST)
self.dot1_mgmt_vid = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_MGMT_VID)
self.dot1_lag = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_LAG)
self.dot1_vlan_names = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES)
self.dot1_proto_vids = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_PROTO_VIDS)
self.dot1_proto_ids = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_PROTO_IDS)
self.dot3_mac_status = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_MAC_STATUS)
self.dot3_max_frame = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME)
self.dot3_power_mdi = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_POWER_MDI)
self.state = None
def __hash__(self):
return self.key.__hash__()
def __eq__(self, rhs):
return (self.key == rhs.key)
def __ne__(self, rhs):
return (self.key != rhs.key or
self.msap != rhs.msap or
self.system_name != rhs.system_name or
self.system_desc != rhs.system_desc or
self.port_desc != rhs.port_desc or
self.capabilities != rhs.capabilities or
self.mgmt_addr != rhs.mgmt_addr or
self.dot1_port_vid != rhs.dot1_port_vid or
self.dot1_vid_digest != rhs.dot1_vid_digest or
self.dot1_mgmt_vid != rhs.dot1_mgmt_vid or
self.dot1_vid_digest != rhs.dot1_vid_digest or
self.dot1_mgmt_vid != rhs.dot1_mgmt_vid or
self.dot1_lag != rhs.dot1_lag or
self.dot1_vlan_names != rhs.dot1_vlan_names or
self.dot1_proto_vids != rhs.dot1_proto_vids or
self.dot1_proto_ids != rhs.dot1_proto_ids or
self.dot3_mac_status != rhs.dot3_mac_status or
self.dot3_max_frame != rhs.dot3_max_frame or
self.dot3_power_mdi != rhs.dot3_power_mdi)
def __str__(self):
return "%s [%s] [%s] [%s], [%s]" % (
self.key, self.system_name, self.system_desc,
self.port_desc, self.capabilities)
def __repr__(self):
return "<Neighbour '%s'>" % str(self)
class LLDPOperator(object):
'''Class to encapsulate LLDP operations for System Inventory'''
def __init__(self, **kwargs):
self._lock = threading.Lock()
self.client = ""
self.agents = []
self.neighbours = []
self.current_neighbours = []
self.previous_neighbours = []
self.current_agents = []
self.previous_agents = []
self.agent_audit_count = 0
self.neighbour_audit_count = 0
def lldpd_get_agent_status(self):
json_obj = json
p = subprocess.Popen(["lldpcli", "-f", "json", "show",
"configuration"],
stdout=subprocess.PIPE)
data = json_obj.loads(p.communicate()[0])
configuration = data['configuration'][0]
config = configuration['config'][0]
rx_only = config['rx-only'][0]
if rx_only.get("value") == "no":
return "rx=enabled,tx=enabled"
else:
return "rx=enabled,tx=disabled"
def lldpd_get_attrs(self, iface):
name_or_uuid = None
chassis_id = None
system_name = None
system_desc = None
capability = None
management_address = None
port_desc = None
dot1_lag = None
dot1_port_vid = None
dot1_vid_digest = None
dot1_mgmt_vid = None
dot1_vlan_names = None
dot1_proto_vids = None
dot1_proto_ids = None
dot3_mac_status = None
dot3_max_frame = None
dot3_power_mdi = None
ttl = None
attrs = {}
# Note: dot1_vid_digest, dot1_mgmt_vid are not currently supported
# by the lldpd daemon
name_or_uuid = iface.get("name")
chassis = iface.get("chassis")[0]
port = iface.get("port")[0]
if not chassis.get('id'):
return attrs
chassis_id = chassis['id'][0].get("value")
if not port.get('id'):
return attrs
port_id = port["id"][0].get("value")
if not port.get('ttl'):
return attrs
ttl = port['ttl'][0].get("value")
if chassis.get("name"):
system_name = chassis['name'][0].get("value")
if chassis.get("descr"):
system_desc = chassis['descr'][0].get("value")
if chassis.get("capability"):
capability = ""
for cap in chassis["capability"]:
if cap.get("enabled"):
if capability:
capability += ", "
capability += cap.get("type").lower()
if chassis.get("mgmt-ip"):
management_address = ""
for addr in chassis["mgmt-ip"]:
if management_address:
management_address += ", "
management_address += addr.get("value").lower()
if port.get("descr"):
port_desc = port["descr"][0].get("value")
if port.get("link-aggregation"):
dot1_lag_supported = port["link-aggregation"][0].get("supported")
dot1_lag_enabled = port["link-aggregation"][0].get("enabled")
dot1_lag = "capable="
if dot1_lag_supported:
dot1_lag += "y,"
else:
dot1_lag += "n,"
dot1_lag += "enabled="
if dot1_lag_enabled:
dot1_lag += "y"
else:
dot1_lag += "n"
if port.get("auto-negotiation"):
port_auto_neg_support = port["auto-negotiation"][0].get(
"supported")
port_auto_neg_enabled = port["auto-negotiation"][0].get("enabled")
dot3_mac_status = "auto-negotiation-capable="
if port_auto_neg_support:
dot3_mac_status += "y,"
else:
dot3_mac_status += "n,"
dot3_mac_status += "auto-negotiation-enabled="
if port_auto_neg_enabled:
dot3_mac_status += "y,"
else:
dot3_mac_status += "n,"
advertised = ""
if port.get("auto-negotiation")[0].get("advertised"):
for adv in port["auto-negotiation"][0].get("advertised"):
if advertised:
advertised += ", "
type = adv.get("type").lower()
if adv.get("hd") and not adv.get("fd"):
type += "hd"
elif adv.get("fd"):
type += "fd"
advertised += type
dot3_mac_status += advertised
if port.get("mfs"):
dot3_max_frame = port["mfs"][0].get("value")
if port.get("power"):
power_mdi_support = port["power"][0].get("supported")
power_mdi_enabled = port["power"][0].get("enabled")
power_mdi_devicetype = port["power"][0].get("device-type")[0].get(
"value")
power_mdi_pairs = port["power"][0].get("pairs")[0].get("value")
power_mdi_class = port["power"][0].get("class")[0].get("value")
dot3_power_mdi = "power-mdi-supported="
if power_mdi_support:
dot3_power_mdi += "y,"
else:
dot3_power_mdi += "n,"
dot3_power_mdi += "power-mdi-enabled="
if power_mdi_enabled:
dot3_power_mdi += "y,"
else:
dot3_power_mdi += "n,"
if power_mdi_support and power_mdi_enabled:
dot3_power_mdi += "device-type=" + power_mdi_devicetype
dot3_power_mdi += ",pairs=" + power_mdi_pairs
dot3_power_mdi += ",class=" + power_mdi_class
vlans = None
if iface.get("vlan"):
vlans = iface.get("vlan")
if vlans:
dot1_vlan_names = ""
for vlan in vlans:
if vlan.get("pvid"):
dot1_port_vid = vlan.get("vlan-id")
continue
if dot1_vlan_names:
dot1_vlan_names += ", "
dot1_vlan_names += vlan.get("value")
ppvids = None
if iface.get("ppvids"):
ppvids = iface.get("ppvid")
if ppvids:
dot1_proto_vids = ""
for ppvid in ppvids:
if dot1_proto_vids:
dot1_proto_vids += ", "
dot1_proto_vids += ppvid.get("value")
pids = None
if iface.get("pi"):
pids = iface.get('pi')
dot1_proto_ids = ""
for id in pids:
if dot1_proto_ids:
dot1_proto_ids += ", "
dot1_proto_ids += id.get("value")
msap = chassis_id + "," + port_id
attrs = {"name_or_uuid": name_or_uuid,
constants.LLDP_TLV_TYPE_CHASSIS_ID: chassis_id,
constants.LLDP_TLV_TYPE_PORT_ID: port_id,
constants.LLDP_TLV_TYPE_TTL: ttl,
"msap": msap,
constants.LLDP_TLV_TYPE_SYSTEM_NAME: system_name,
constants.LLDP_TLV_TYPE_SYSTEM_DESC: system_desc,
constants.LLDP_TLV_TYPE_SYSTEM_CAP: capability,
constants.LLDP_TLV_TYPE_MGMT_ADDR: management_address,
constants.LLDP_TLV_TYPE_PORT_DESC: port_desc,
constants.LLDP_TLV_TYPE_DOT1_LAG: dot1_lag,
constants.LLDP_TLV_TYPE_DOT1_PORT_VID: dot1_port_vid,
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST: dot1_vid_digest,
constants.LLDP_TLV_TYPE_DOT1_MGMT_VID: dot1_mgmt_vid,
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES: dot1_vlan_names,
constants.LLDP_TLV_TYPE_DOT1_PROTO_VIDS: dot1_proto_vids,
constants.LLDP_TLV_TYPE_DOT1_PROTO_IDS: dot1_proto_ids,
constants.LLDP_TLV_TYPE_DOT3_MAC_STATUS: dot3_mac_status,
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME: dot3_max_frame,
constants.LLDP_TLV_TYPE_DOT3_POWER_MDI: dot3_power_mdi}
return attrs
def lldpd_has_neighbour(self, name):
'''check if the interface has LLDP neighbours'''
p = subprocess.check_output(["lldpcli", "-f", "keyvalue", "show",
"neighbors", "summary", "ports", name])
return len(p) > 0
def lldpd_agent_list(self):
json_obj = json
lldp_agents = []
p = subprocess.Popen(["lldpcli", "-f", "json", "show", "interface",
"detail"], stdout=subprocess.PIPE)
data = json_obj.loads(p.communicate()[0])
lldp = data['lldp'][0]
if not lldp.get('interface'):
return lldp_agents
for iface in lldp['interface']:
agent_attrs = self.lldpd_get_attrs(iface)
status = self.lldpd_get_agent_status()
agent_attrs.update({"status": status})
agent = Agent(**agent_attrs)
lldp_agents.append(agent)
return lldp_agents
def lldpd_neighbour_list(self):
json_obj = json
lldp_neighbours = []
p = subprocess.Popen(["lldpcli", "-f", "json", "show", "neighbor",
"detail"], stdout=subprocess.PIPE)
data = json_obj.loads(p.communicate()[0])
lldp = data['lldp'][0]
if not lldp.get('interface'):
return lldp_neighbours
for iface in lldp['interface']:
neighbour_attrs = self.lldpd_get_attrs(iface)
neighbour = Neighbour(**neighbour_attrs)
lldp_neighbours.append(neighbour)
return lldp_neighbours
def _do_request(self, callable):
"""Thread safe wrapper for executing client requests.
"""
with self._lock:
return callable()
def _execute_lldp_request(self, callable, snat=None):
try:
return self._do_request(callable)
except Exception as e:
LOG.error("Failed to execute LLDP request: %s", str(e))
def vswitch_lldp_get_status(self, admin_status):
if admin_status == "enabled":
status = "rx=enabled,tx=enabled"
elif admin_status == "tx-only":
status = "rx=disabled,tx=enabled"
elif admin_status == "rx-only":
status = "rx=enabled,tx=disabled"
else:
status = "rx=disabled,tx=disabled"
return status
def vswitch_lldp_get_attrs(self, agent_neighbour_dict):
attrs = {}
vswitch_to_db_dict = {'local-chassis':
constants.LLDP_TLV_TYPE_CHASSIS_ID,
'local-port': constants.LLDP_TLV_TYPE_PORT_ID,
'remote-chassis':
constants.LLDP_TLV_TYPE_CHASSIS_ID,
'remote-port': constants.LLDP_TLV_TYPE_PORT_ID,
'tx-ttl': constants.LLDP_TLV_TYPE_TTL,
'rx-ttl': constants.LLDP_TLV_TYPE_TTL,
'system-name':
constants.LLDP_TLV_TYPE_SYSTEM_NAME,
'system-description':
constants.LLDP_TLV_TYPE_SYSTEM_DESC,
'port-description':
constants.LLDP_TLV_TYPE_PORT_DESC,
'system-capabilities':
constants.LLDP_TLV_TYPE_SYSTEM_CAP,
'management-address':
constants.LLDP_TLV_TYPE_MGMT_ADDR,
'dot1-lag': constants.LLDP_TLV_TYPE_DOT1_LAG,
'dot1-management-vid':
constants.LLDP_TLV_TYPE_DOT1_MGMT_VID,
'dot1-port-vid':
constants.LLDP_TLV_TYPE_DOT1_PORT_VID,
'dot1-proto-ids':
constants.LLDP_TLV_TYPE_DOT1_PROTO_IDS,
'dot1-proto-vids':
constants.LLDP_TLV_TYPE_DOT1_PROTO_VIDS,
'dot1-vid-digest':
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST,
'dot1-vlan-names':
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES,
'dot3-lag':
constants.LLDP_TLV_TYPE_DOT1_LAG,
'dot3-mac-status':
constants.LLDP_TLV_TYPE_DOT3_MAC_STATUS,
'dot3-max-frame':
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME,
'dot3-power-mdi':
constants.LLDP_TLV_TYPE_DOT3_POWER_MDI}
for k, v in vswitch_to_db_dict.iteritems():
if k in agent_neighbour_dict:
if agent_neighbour_dict[k]:
attr = {v: agent_neighbour_dict[k]}
else:
attr = {v: None}
attrs.update(attr)
msap = attrs[constants.LLDP_TLV_TYPE_CHASSIS_ID] \
+ "," + attrs[constants.LLDP_TLV_TYPE_PORT_ID]
attr = {"name_or_uuid": agent_neighbour_dict["port-uuid"],
"msap": msap}
attrs.update(attr)
return attrs
def vswitch_lldp_agent_list(self):
"""Sends a request to the vswitch requesting the full list of LLDP agent
entries.
"""
LOG.error("vswitch_lldp_agent_list is not implemented.")
return []
def vswitch_lldp_neighbour_list(self):
"""Sends a request to the vswitch requesting the full list of LLDP
neighbour entries.
"""
LOG.error("vswitch_lldp_neighbour_ist s not implemented.")
return []
def lldp_agents_list(self, do_compute=False):
self.agent_audit_count += 1
if self.agent_audit_count > constants.LLDP_FULL_AUDIT_COUNT:
LOG.debug("LLDP agent audit: triggering full sync")
self.agent_audit_count = 0
self.lldp_agents_clear()
self.previous_agents = self.current_agents
self.current_agents = self.lldpd_agent_list()
if do_compute:
self.current_agents += self.vswitch_lldp_agent_list()
current = set(self.current_agents)
previous = set(self.previous_agents)
removed = previous - current
agent_array = []
for a in self.current_agents:
agent_array.append(a)
if removed:
for r in removed:
LOG.debug("LLDP agent audit: detected removed agent")
r.state = constants.LLDP_AGENT_STATE_REMOVED
agent_array.append(r)
return agent_array
# Check that there is actual state changes and return an empty list if
# nothing changed.
if self.previous_agents:
pairs = zip(sorted(current, key=attrgetter('key')),
sorted(previous, key=attrgetter('key')))
if not any(x != y for x, y in pairs):
LOG.debug("LLDP agent audit: No changes")
return []
return agent_array
def lldp_agents_clear(self):
self.current_agents = []
self.previous_agents = []
def lldp_neighbours_list(self, do_compute=False):
self.neighbour_audit_count += 1
if self.neighbour_audit_count > constants.LLDP_FULL_AUDIT_COUNT:
LOG.debug("LLDP neighbour audit: triggering full sync")
self.neighbour_audit_count = 0
self.lldp_neighbours_clear()
self.previous_neighbours = self.current_neighbours
self.current_neighbours = self.lldpd_neighbour_list()
if do_compute:
self.current_neighbours += self.vswitch_lldp_neighbour_list()
current = set(self.current_neighbours)
previous = set(self.previous_neighbours)
removed = previous - current
neighbour_array = []
for n in self.current_neighbours:
neighbour_array.append(n)
if removed:
for r in removed:
LOG.debug("LLDP neighbour audit: detected removed neighbour")
r.state = constants.LLDP_NEIGHBOUR_STATE_REMOVED
neighbour_array.append(r)
return neighbour_array
# Check that there is actual state changes and return an empty list if
# nothing changed.
if self.previous_neighbours:
pairs = zip(sorted(current, key=attrgetter('key')),
sorted(previous, key=attrgetter('key')))
if not any(x != y for x, y in pairs):
LOG.debug("LLDP neighbour audit: No changes")
return []
return neighbour_array
def lldp_neighbours_clear(self):
self.current_neighbours = []
self.previous_neighbours = []
def lldp_update_systemname(self, context, systemname, do_compute=False):
p = subprocess.Popen(["lldpcli", "-f", "json", "show", "chassis"],
stdout=subprocess.PIPE)
data = json.loads(p.communicate()[0])
local_chassis = data['local-chassis'][0]
chassis = local_chassis['chassis'][0]
name = chassis.get('name', None)
if name is None or not name[0].get("value"):
return
name = name[0]
hostname = name.get("value").partition(':')[0]
newname = hostname + ":" + systemname
p = subprocess.Popen(["lldpcli", "configure", "system", "hostname",
newname], stdout=subprocess.PIPE)
if do_compute:
LOG.error("lldp_update_systemname failed due to lack of vswitch")

View File

@ -0,0 +1,23 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
from oslo_config import cfg
from oslo_utils._i18n import _
SYSINV_LLDP_OPTS = [
cfg.ListOpt('drivers',
default=['lldpd'],
help=_("An ordered list of sysinv LLDP driver "
"entrypoints to be loaded from the "
"sysinv.agent namespace.")),
]
cfg.CONF.register_opts(SYSINV_LLDP_OPTS, group="lldp")

View File

@ -0,0 +1,47 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class SysinvLldpDriverBase(object):
"""Sysinv LLDP Driver Base Class."""
@abc.abstractmethod
def lldp_has_neighbour(self, name):
pass
@abc.abstractmethod
def lldp_update(self):
pass
@abc.abstractmethod
def lldp_agents_list(self):
pass
@abc.abstractmethod
def lldp_neighbours_list(self):
pass
@abc.abstractmethod
def lldp_agents_clear(self):
pass
@abc.abstractmethod
def lldp_neighbours_clear(self):
pass
@abc.abstractmethod
def lldp_update_systemname(self, systemname):
pass

View File

@ -0,0 +1,321 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
from oslo_log import log as logging
import simplejson as json
import subprocess
from sysinv.agent.lldp.drivers import base
from sysinv.agent.lldp import plugin
from sysinv.common import constants
LOG = logging.getLogger(__name__)
class SysinvLldpdAgentDriver(base.SysinvLldpDriverBase):
def __init__(self, **kwargs):
self.client = ""
self.agents = []
self.neighbours = []
self.current_neighbours = []
self.previous_neighbours = []
self.current_agents = []
self.previous_agents = []
self.agent_audit_count = 0
self.neighbour_audit_count = 0
def initialize(self):
self.__init__()
@staticmethod
def _lldpd_get_agent_status():
json_obj = json
p = subprocess.Popen(["lldpcli", "-f", "json", "show",
"configuration"],
stdout=subprocess.PIPE)
data = json_obj.loads(p.communicate()[0])
configuration = data['configuration'][0]
config = configuration['config'][0]
rx_only = config['rx-only'][0]
if rx_only.get("value") == "no":
return "rx=enabled,tx=enabled"
else:
return "rx=enabled,tx=disabled"
@staticmethod
def _lldpd_get_attrs(iface):
name_or_uuid = None
chassis_id = None
system_name = None
system_desc = None
capability = None
management_address = None
port_desc = None
dot1_lag = None
dot1_port_vid = None
dot1_vid_digest = None
dot1_mgmt_vid = None
dot1_vlan_names = None
dot1_proto_vids = None
dot1_proto_ids = None
dot3_mac_status = None
dot3_max_frame = None
dot3_power_mdi = None
ttl = None
attrs = {}
# Note: dot1_vid_digest, dot1_mgmt_vid are not currently supported
# by the lldpd daemon
name_or_uuid = iface.get("name")
chassis = iface.get("chassis")[0]
port = iface.get("port")[0]
if not chassis.get('id'):
return attrs
chassis_id = chassis['id'][0].get("value")
if not port.get('id'):
return attrs
port_id = port["id"][0].get("value")
if not port.get('ttl'):
return attrs
ttl = port['ttl'][0].get("value")
if chassis.get("name"):
system_name = chassis['name'][0].get("value")
if chassis.get("descr"):
system_desc = chassis['descr'][0].get("value")
if chassis.get("capability"):
capability = ""
for cap in chassis["capability"]:
if cap.get("enabled"):
if capability:
capability += ", "
capability += cap.get("type").lower()
if chassis.get("mgmt-ip"):
management_address = ""
for addr in chassis["mgmt-ip"]:
if management_address:
management_address += ", "
management_address += addr.get("value").lower()
if port.get("descr"):
port_desc = port["descr"][0].get("value")
if port.get("link-aggregation"):
dot1_lag_supported = port["link-aggregation"][0].get("supported")
dot1_lag_enabled = port["link-aggregation"][0].get("enabled")
dot1_lag = "capable="
if dot1_lag_supported:
dot1_lag += "y,"
else:
dot1_lag += "n,"
dot1_lag += "enabled="
if dot1_lag_enabled:
dot1_lag += "y"
else:
dot1_lag += "n"
if port.get("auto-negotiation"):
port_auto_neg_support = port["auto-negotiation"][0].get(
"supported")
port_auto_neg_enabled = port["auto-negotiation"][0].get("enabled")
dot3_mac_status = "auto-negotiation-capable="
if port_auto_neg_support:
dot3_mac_status += "y,"
else:
dot3_mac_status += "n,"
dot3_mac_status += "auto-negotiation-enabled="
if port_auto_neg_enabled:
dot3_mac_status += "y,"
else:
dot3_mac_status += "n,"
advertised = ""
if port.get("auto-negotiation")[0].get("advertised"):
for adv in port["auto-negotiation"][0].get("advertised"):
if advertised:
advertised += ", "
type = adv.get("type").lower()
if adv.get("hd") and not adv.get("fd"):
type += "hd"
elif adv.get("fd"):
type += "fd"
advertised += type
dot3_mac_status += advertised
if port.get("mfs"):
dot3_max_frame = port["mfs"][0].get("value")
if port.get("power"):
power_mdi_support = port["power"][0].get("supported")
power_mdi_enabled = port["power"][0].get("enabled")
power_mdi_devicetype = port["power"][0].get("device-type")[0].get(
"value")
power_mdi_pairs = port["power"][0].get("pairs")[0].get("value")
power_mdi_class = port["power"][0].get("class")[0].get("value")
dot3_power_mdi = "power-mdi-supported="
if power_mdi_support:
dot3_power_mdi += "y,"
else:
dot3_power_mdi += "n,"
dot3_power_mdi += "power-mdi-enabled="
if power_mdi_enabled:
dot3_power_mdi += "y,"
else:
dot3_power_mdi += "n,"
if power_mdi_support and power_mdi_enabled:
dot3_power_mdi += "device-type=" + power_mdi_devicetype
dot3_power_mdi += ",pairs=" + power_mdi_pairs
dot3_power_mdi += ",class=" + power_mdi_class
vlans = None
if iface.get("vlan"):
vlans = iface.get("vlan")
if vlans:
dot1_vlan_names = ""
for vlan in vlans:
if vlan.get("pvid"):
dot1_port_vid = vlan.get("vlan-id")
continue
if dot1_vlan_names:
dot1_vlan_names += ", "
dot1_vlan_names += vlan.get("value")
ppvids = None
if iface.get("ppvids"):
ppvids = iface.get("ppvid")
if ppvids:
dot1_proto_vids = ""
for ppvid in ppvids:
if dot1_proto_vids:
dot1_proto_vids += ", "
dot1_proto_vids += ppvid.get("value")
pids = None
if iface.get("pi"):
pids = iface.get('pi')
dot1_proto_ids = ""
for id in pids:
if dot1_proto_ids:
dot1_proto_ids += ", "
dot1_proto_ids += id.get("value")
msap = chassis_id + "," + port_id
attrs = {"name_or_uuid": name_or_uuid,
constants.LLDP_TLV_TYPE_CHASSIS_ID: chassis_id,
constants.LLDP_TLV_TYPE_PORT_ID: port_id,
constants.LLDP_TLV_TYPE_TTL: ttl,
"msap": msap,
constants.LLDP_TLV_TYPE_SYSTEM_NAME: system_name,
constants.LLDP_TLV_TYPE_SYSTEM_DESC: system_desc,
constants.LLDP_TLV_TYPE_SYSTEM_CAP: capability,
constants.LLDP_TLV_TYPE_MGMT_ADDR: management_address,
constants.LLDP_TLV_TYPE_PORT_DESC: port_desc,
constants.LLDP_TLV_TYPE_DOT1_LAG: dot1_lag,
constants.LLDP_TLV_TYPE_DOT1_PORT_VID: dot1_port_vid,
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST: dot1_vid_digest,
constants.LLDP_TLV_TYPE_DOT1_MGMT_VID: dot1_mgmt_vid,
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES: dot1_vlan_names,
constants.LLDP_TLV_TYPE_DOT1_PROTO_VIDS: dot1_proto_vids,
constants.LLDP_TLV_TYPE_DOT1_PROTO_IDS: dot1_proto_ids,
constants.LLDP_TLV_TYPE_DOT3_MAC_STATUS: dot3_mac_status,
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME: dot3_max_frame,
constants.LLDP_TLV_TYPE_DOT3_POWER_MDI: dot3_power_mdi}
return attrs
def lldp_has_neighbour(self, name):
p = subprocess.check_output(["lldpcli", "-f", "keyvalue", "show",
"neighbors", "summary", "ports", name])
return len(p) > 0
def lldp_update(self):
subprocess.call(['lldpcli', 'update'])
def lldp_agents_list(self):
json_obj = json
lldp_agents = []
p = subprocess.Popen(["lldpcli", "-f", "json", "show", "interface",
"detail"], stdout=subprocess.PIPE)
data = json_obj.loads(p.communicate()[0])
lldp = data['lldp'][0]
if not lldp.get('interface'):
return lldp_agents
for iface in lldp['interface']:
agent_attrs = self._lldpd_get_attrs(iface)
status = self._lldpd_get_agent_status()
agent_attrs.update({"status": status})
agent = plugin.Agent(**agent_attrs)
lldp_agents.append(agent)
return lldp_agents
def lldp_agents_clear(self):
self.current_agents = []
self.previous_agents = []
def lldp_neighbours_list(self):
json_obj = json
lldp_neighbours = []
p = subprocess.Popen(["lldpcli", "-f", "json", "show", "neighbor",
"detail"], stdout=subprocess.PIPE)
data = json_obj.loads(p.communicate()[0])
lldp = data['lldp'][0]
if not lldp.get('interface'):
return lldp_neighbours
for iface in lldp['interface']:
neighbour_attrs = self._lldpd_get_attrs(iface)
neighbour = plugin.Neighbour(**neighbour_attrs)
lldp_neighbours.append(neighbour)
return lldp_neighbours
def lldp_neighbours_clear(self):
self.current_neighbours = []
self.previous_neighbours = []
def lldp_update_systemname(self, systemname):
p = subprocess.Popen(["lldpcli", "-f", "json", "show", "chassis"],
stdout=subprocess.PIPE)
data = json.loads(p.communicate()[0])
local_chassis = data['local-chassis'][0]
chassis = local_chassis['chassis'][0]
name = chassis.get('name', None)
if name is None or not name[0].get("value"):
return
name = name[0]
hostname = name.get("value").partition(':')[0]
newname = hostname + ":" + systemname
p = subprocess.Popen(["lldpcli", "configure", "system", "hostname",
newname], stdout=subprocess.PIPE)

View File

@ -0,0 +1,166 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
import simplejson as json
import subprocess
from oslo_log import log as logging
from sysinv.agent.lldp.drivers.lldpd import driver as lldpd_driver
from sysinv.common import constants
LOG = logging.getLogger(__name__)
class SysinvOVSAgentDriver(lldpd_driver.SysinvLldpdAgentDriver):
def run_cmd(self, cmd):
p = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
p.wait()
output, error = p.communicate()
if p.returncode != 0:
LOG.error("Failed to run command %s: error: %s", cmd, error)
return None
return output
def lldp_ovs_get_interface_port_map(self):
interface_port_map = {}
cmd = "ovs-vsctl --timeout 10 --format json "\
"--columns name,_uuid,interfaces list Port"
output = self.run_cmd(cmd)
if not output:
return
ports = json.loads(output)
ports = ports['data']
for port in ports:
port_uuid = port[1][1]
interfaces = port[2][1]
if isinstance(interfaces, list):
for interface in interfaces:
interface_uuid = interface[1]
interface_port_map[interface_uuid] = port_uuid
else:
interface_uuid = interfaces
interface_port_map[interface_uuid] = port_uuid
return interface_port_map
def lldp_ovs_get_port_bridge_map(self):
port_bridge_map = {}
cmd = "ovs-vsctl --timeout 10 --format json "\
"--columns name,ports list Bridge"
output = self.run_cmd(cmd)
if not output:
return
bridges = json.loads(output)
bridges = bridges['data']
for bridge in bridges:
bridge_name = bridge[0]
port_set = bridge[1][1]
for port in port_set:
value = port[1]
port_bridge_map[value] = bridge_name
return port_bridge_map
def lldp_ovs_lldp_flow_exists(self, brname, in_port):
cmd = "ovs-ofctl dump-flows {} in_port={},dl_dst={},dl_type={}".format(
brname, in_port, constants.LLDP_MULTICAST_ADDRESS,
constants.LLDP_ETHER_TYPE)
output = self.run_cmd(cmd)
if not output:
return None
return (output.count("\n") > 1)
def lldp_ovs_add_flows(self, brname, in_port, out_port):
cmd = ("ovs-ofctl add-flow {} in_port={},dl_dst={},dl_type={},"
"actions=output:{}".format(
brname, in_port, constants.LLDP_MULTICAST_ADDRESS,
constants.LLDP_ETHER_TYPE, out_port))
output = self.run_cmd(cmd)
if not output:
return
cmd = ("ovs-ofctl add-flow {} in_port={},dl_dst={},dl_type={},"
"actions=output:{}".format(
brname, out_port, constants.LLDP_MULTICAST_ADDRESS,
constants.LLDP_ETHER_TYPE, in_port))
output = self.run_cmd(cmd)
if not output:
return
def lldp_ovs_update_flows(self):
port_bridge_map = self.lldp_ovs_get_port_bridge_map()
if not port_bridge_map:
return
interface_port_map = self.lldp_ovs_get_interface_port_map()
if not interface_port_map:
return
cmd = "ovs-vsctl --timeout 10 --format json "\
"--columns name,_uuid,type,other_config list Interface"
output = self.run_cmd(cmd)
if not output:
return
data = json.loads(output)
data = data['data']
for interface in data:
name = interface[0]
uuid = interface[1][1]
type = interface[2]
other_config = interface[3]
if type != 'internal':
continue
config_map = other_config[1]
for config in config_map:
key = config[0]
value = config[1]
if key != 'lldp_phy_peer':
continue
phy_peer = value
brname = port_bridge_map[interface_port_map[uuid]]
if not self.lldp_ovs_lldp_flow_exists(brname, name):
LOG.info("Adding missing LLDP flow from %s to %s",
name, phy_peer)
self.lldp_ovs_add_flows(brname, name, phy_peer)
if not self.lldp_ovs_lldp_flow_exists(brname, value):
LOG.info("Adding missing LLDP flow from %s to %s",
phy_peer, name)
self.lldp_ovs_add_flows(brname, phy_peer, name)
def lldp_agents_list(self):
self.lldp_ovs_update_flows()
return lldpd_driver.SysinvLldpdAgentDriver.lldp_agents_list(self)
def lldp_neighbours_list(self):
self.lldp_ovs_update_flows()
return lldpd_driver.SysinvLldpdAgentDriver.lldp_neighbours_list(self)

View File

@ -0,0 +1,176 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
from oslo_config import cfg
from oslo_log import log
from stevedore.named import NamedExtensionManager
from sysinv.common import exception
LOG = log.getLogger(__name__)
cfg.CONF.import_opt('drivers',
'sysinv.agent.lldp.config',
group='lldp')
class SysinvLldpDriverManager(NamedExtensionManager):
"""Implementation of Sysinv LLDP drivers."""
def __init__(self, namespace='sysinv.agent.lldp.drivers'):
# Registered sysinv lldp agent drivers, keyed by name.
self.drivers = {}
# Ordered list of sysinv lldp agent drivers, defining
# the order in which the drivers are called.
self.ordered_drivers = []
names = cfg.CONF.lldp.drivers
LOG.info("Configured sysinv LLDP agent drivers: %s", names)
super(SysinvLldpDriverManager, self).__init__(
namespace,
names,
invoke_on_load=True,
name_order=True)
LOG.info("Loaded sysinv LLDP agent drivers: %s", self.names())
self._register_drivers()
def _register_drivers(self):
"""Register all sysinv LLDP agent drivers.
This method should only be called once in the
SysinvLldpDriverManager constructor.
"""
for ext in self:
self.drivers[ext.name] = ext
self.ordered_drivers.append(ext)
LOG.info("Registered sysinv LLDP agent drivers: %s",
[driver.name for driver in self.ordered_drivers])
def _call_drivers_and_return_array(self, method_name, attr=None,
raise_orig_exc=False):
"""Helper method for calling a method across all drivers.
:param method_name: name of the method to call
:param attr: an optional attribute to provide to the drivers
:param raise_orig_exc: whether or not to raise the original
driver exception, or use a general one
"""
ret = []
for driver in self.ordered_drivers:
try:
method = getattr(driver.obj, method_name)
if attr:
ret = ret + method(attr)
else:
ret = ret + method()
except Exception as e:
LOG.exception(e)
LOG.error(
"Sysinv LLDP agent driver '%(name)s' "
"failed in %(method)s",
{'name': driver.name, 'method': method_name}
)
if raise_orig_exc:
raise
else:
raise exception.LLDPDriverError(
method=method_name
)
return list(set(ret))
def _call_drivers(self, method_name, attr=None, raise_orig_exc=False):
"""Helper method for calling a method across all drivers.
:param method_name: name of the method to call
:param attr: an optional attribute to provide to the drivers
:param raise_orig_exc: whether or not to raise the original
driver exception, or use a general one
"""
for driver in self.ordered_drivers:
try:
method = getattr(driver.obj, method_name)
if attr:
return method(attr)
else:
return method()
except Exception as e:
LOG.exception(e)
LOG.error(
"Sysinv LLDP agent driver '%(name)s' "
"failed in %(method)s",
{'name': driver.name, 'method': method_name}
)
if raise_orig_exc:
raise
else:
raise exception.LLDPDriverError(
method=method_name
)
def lldp_has_neighbour(self, name):
try:
return self._call_drivers("lldp_has_neighbour",
attr=name,
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return []
def lldp_update(self):
try:
return self._call_drivers("lldp_update",
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return []
def lldp_agents_list(self):
try:
return self._call_drivers_and_return_array("lldp_agents_list",
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return []
def lldp_neighbours_list(self):
try:
return self._call_drivers_and_return_array("lldp_neighbours_list",
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return []
def lldp_agents_clear(self):
try:
return self._call_drivers("lldp_agents_clear",
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return
def lldp_neighbours_clear(self):
try:
return self._call_drivers("lldp_neighbours_clear",
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return
def lldp_update_systemname(self, systemname):
try:
return self._call_drivers("lldp_update_systemname",
attr=systemname,
raise_orig_exc=True)
except Exception as e:
LOG.exception(e)
return

View File

@ -0,0 +1,245 @@
#
# Copyright (c) 2018 Wind River Systems, Inc.
#
# SPDX-License-Identifier: Apache-2.0
#
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# All Rights Reserved.
#
from oslo_log import log as logging
from sysinv.agent.lldp import manager
from sysinv.common import exception
from sysinv.common import constants
from sysinv.openstack.common import excutils
LOG = logging.getLogger(__name__)
class Key(object):
def __init__(self, chassisid, portid, portname):
self.chassisid = chassisid
self.portid = portid
self.portname = portname
def __hash__(self):
return hash((self.chassisid, self.portid, self.portname))
def __cmp__(self, rhs):
return (cmp(self.chassisid, rhs.chassisid) or
cmp(self.portid, rhs.portid) or
cmp(self.portname, rhs.portname))
def __eq__(self, rhs):
return (self.chassisid == rhs.chassisid and
self.portid == rhs.portid and
self.portname == rhs.portname)
def __ne__(self, rhs):
return (self.chassisid != rhs.chassisid or
self.portid != rhs.portid or
self.portname != rhs.portname)
def __str__(self):
return "%s [%s] [%s]" % (self.portname, self.chassisid, self.portid)
def __repr__(self):
return "<Key '%s'>" % str(self)
class Agent(object):
'''Class to encapsulate LLDP agent data for System Inventory'''
def __init__(self, **kwargs):
'''Construct an Agent object with the given values.'''
self.key = Key(kwargs.get(constants.LLDP_TLV_TYPE_CHASSIS_ID),
kwargs.get(constants.LLDP_TLV_TYPE_PORT_ID),
kwargs.get("name_or_uuid"))
self.status = kwargs.get('status')
self.ttl = kwargs.get(constants.LLDP_TLV_TYPE_TTL)
self.system_name = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_NAME)
self.system_desc = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_DESC)
self.port_desc = kwargs.get(constants.LLDP_TLV_TYPE_PORT_DESC)
self.capabilities = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_CAP)
self.mgmt_addr = kwargs.get(constants.LLDP_TLV_TYPE_MGMT_ADDR)
self.dot1_lag = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_LAG)
self.dot1_vlan_names = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES)
self.dot3_max_frame = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME)
self.state = None
def __hash__(self):
return self.key.__hash__()
def __eq__(self, rhs):
return (self.key == rhs.key)
def __ne__(self, rhs):
return (self.key != rhs.key or
self.status != rhs.status or
self.ttl != rhs.ttl or
self.system_name != rhs.system_name or
self.system_desc != rhs.system_desc or
self.port_desc != rhs.port_desc or
self.capabilities != rhs.capabilities or
self.mgmt_addr != rhs.mgmt_addr or
self.dot1_lag != rhs.dot1_lag or
self.dot1_vlan_names != rhs.dot1_vlan_names or
self.dot3_max_frame != rhs.dot3_max_frame or
self.state != rhs.state)
def __str__(self):
return "%s: [%s] [%s] [%s], [%s], [%s], [%s], [%s], [%s]" % (
self.key, self.status, self.system_name, self.system_desc,
self.port_desc, self.capabilities,
self.mgmt_addr, self.dot1_lag,
self.dot3_max_frame)
def __repr__(self):
return "<Agent '%s'>" % str(self)
class Neighbour(object):
'''Class to encapsulate LLDP neighbour data for System Inventory'''
def __init__(self, **kwargs):
'''Construct an Neighbour object with the given values.'''
self.key = Key(kwargs.get(constants.LLDP_TLV_TYPE_CHASSIS_ID),
kwargs.get(constants.LLDP_TLV_TYPE_PORT_ID),
kwargs.get("name_or_uuid"))
self.msap = kwargs.get('msap')
self.ttl = kwargs.get(constants.LLDP_TLV_TYPE_TTL)
self.system_name = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_NAME)
self.system_desc = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_DESC)
self.port_desc = kwargs.get(constants.LLDP_TLV_TYPE_PORT_DESC)
self.capabilities = kwargs.get(constants.LLDP_TLV_TYPE_SYSTEM_CAP)
self.mgmt_addr = kwargs.get(constants.LLDP_TLV_TYPE_MGMT_ADDR)
self.dot1_port_vid = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_PORT_VID)
self.dot1_vid_digest = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST)
self.dot1_mgmt_vid = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_MGMT_VID)
self.dot1_vid_digest = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VID_DIGEST)
self.dot1_mgmt_vid = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_MGMT_VID)
self.dot1_lag = kwargs.get(constants.LLDP_TLV_TYPE_DOT1_LAG)
self.dot1_vlan_names = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_VLAN_NAMES)
self.dot1_proto_vids = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_PROTO_VIDS)
self.dot1_proto_ids = kwargs.get(
constants.LLDP_TLV_TYPE_DOT1_PROTO_IDS)
self.dot3_mac_status = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_MAC_STATUS)
self.dot3_max_frame = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_MAX_FRAME)
self.dot3_power_mdi = kwargs.get(
constants.LLDP_TLV_TYPE_DOT3_POWER_MDI)
self.state = None
def __hash__(self):
return self.key.__hash__()
def __eq__(self, rhs):
return (self.key == rhs.key)
def __ne__(self, rhs):
return (self.key != rhs.key or
self.msap != rhs.msap or
self.system_name != rhs.system_name or
self.system_desc != rhs.system_desc or
self.port_desc != rhs.port_desc or
self.capabilities != rhs.capabilities or
self.mgmt_addr != rhs.mgmt_addr or
self.dot1_port_vid != rhs.dot1_port_vid or
self.dot1_vid_digest != rhs.dot1_vid_digest or
self.dot1_mgmt_vid != rhs.dot1_mgmt_vid or
self.dot1_vid_digest != rhs.dot1_vid_digest or
self.dot1_mgmt_vid != rhs.dot1_mgmt_vid or
self.dot1_lag != rhs.dot1_lag or
self.dot1_vlan_names != rhs.dot1_vlan_names or
self.dot1_proto_vids != rhs.dot1_proto_vids or
self.dot1_proto_ids != rhs.dot1_proto_ids or
self.dot3_mac_status != rhs.dot3_mac_status or
self.dot3_max_frame != rhs.dot3_max_frame or
self.dot3_power_mdi != rhs.dot3_power_mdi)
def __str__(self):
return "%s [%s] [%s] [%s], [%s]" % (
self.key, self.system_name, self.system_desc,
self.port_desc, self.capabilities)
def __repr__(self):
return "<Neighbour '%s'>" % str(self)
class SysinvLldpPlugin():
"""Implementation of the Plugin."""
def __init__(self):
self.manager = manager.SysinvLldpDriverManager()
def lldp_has_neighbour(self, name):
try:
return self.manager.lldp_has_neighbour(name)
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP has neighbour failed")
def lldp_update(self):
try:
self.manager.lldp_update()
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP update failed")
def lldp_agents_list(self):
try:
agents = self.manager.lldp_agents_list()
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP agents list failed")
return agents
def lldp_agents_clear(self):
try:
self.manager.lldp_agents_clear()
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP agents clear failed")
def lldp_neighbours_list(self):
try:
neighbours = self.manager.lldp_neighbours_list()
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP neighbours list failed")
return neighbours
def lldp_neighbours_clear(self):
try:
self.manager.lldp_neighbours_clear()
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP neighbours clear failed")
def lldp_update_systemname(self, systemname):
try:
self.manager.lldp_update_systemname(systemname)
except exception.LLDPDriverError as e:
LOG.exception(e)
with excutils.save_and_reraise_exception():
LOG.error("LLDP update systemname failed")

View File

@ -53,7 +53,7 @@ from sysinv.agent import pv
from sysinv.agent import lvg
from sysinv.agent import pci
from sysinv.agent import node
from sysinv.agent import lldp
from sysinv.agent.lldp import plugin as lldp_plugin
from sysinv.common import constants
from sysinv.common import exception
from sysinv.common import service
@ -138,7 +138,7 @@ class AgentManager(service.PeriodicService):
self._ipv_operator = pv.PVOperator()
self._ipartition_operator = partition.PartitionOperator()
self._ilvg_operator = lvg.LVGOperator()
self._lldp_operator = lldp.LLDPOperator()
self._lldp_operator = lldp_plugin.SysinvLldpPlugin()
self._iconfig_read_config_reported = None
self._ihost_personality = None
self._ihost_uuid = ""
@ -363,10 +363,8 @@ class AgentManager(service.PeriodicService):
neighbours = []
agents = []
do_compute = constants.COMPUTE in self.subfunctions_list_get()
try:
neighbours = self._lldp_operator.lldp_neighbours_list(do_compute)
neighbours = self._lldp_operator.lldp_neighbours_list()
except Exception as e:
LOG.error("Failed to get LLDP neighbours: %s", str(e))
@ -408,7 +406,7 @@ class AgentManager(service.PeriodicService):
pass
try:
agents = self._lldp_operator.lldp_agents_list(do_compute)
agents = self._lldp_operator.lldp_agents_list()
except Exception as e:
LOG.error("Failed to get LLDP agents: %s", str(e))
@ -470,7 +468,8 @@ class AgentManager(service.PeriodicService):
subprocess.call(['ip', 'link', 'set', interface, 'up'])
links_down.append(interface)
LOG.info('interface %s enabled to receive LLDP PDUs' % interface)
subprocess.call(['lldpcli', 'update'])
self._lldp_operator.lldp_update()
# delay maximum 30 seconds for lldpd to receive LLDP PDU
timeout = 0
link_wait_for_lldp = True
@ -478,8 +477,9 @@ class AgentManager(service.PeriodicService):
time.sleep(5)
timeout = timeout + 5
link_wait_for_lldp = False
for link in links_down:
if not self._lldp_operator.lldpd_has_neighbour(link):
if not self._lldp_operator.lldp_has_neighbour(link):
link_wait_for_lldp = True
break
self.host_lldp_get_and_report(context, rpcapi, host_uuid)
@ -1261,12 +1261,10 @@ class AgentManager(service.PeriodicService):
:param systemname: the systemname
"""
do_compute = constants.COMPUTE in self.subfunctions_list_get()
rpcapi = conductor_rpcapi.ConductorAPI(
topic=conductor_rpcapi.MANAGER_TOPIC)
# Update the lldp agent
self._lldp_operator.lldp_update_systemname(context, systemname,
do_compute)
self._lldp_operator.lldp_update_systemname(systemname)
# Trigger an audit to ensure the db is up to date
self.host_lldp_get_and_report(context, rpcapi, self._ihost_uuid)

View File

@ -566,6 +566,10 @@ class LLDPTlvExists(Conflict):
message = _("An LLDP TLV with type %(type) already exists.")
class LLDPDriverError(Conflict):
message = _("An LLDP driver error has occurred. method=%(method)")
class SDNControllerAlreadyExists(Conflict):
message = _("An SDN Controller with uuid %(uuid)s already exists.")