Add the monitor command
- monitor command role is to print on a regular base the values of variable constants (Temperature, power, Fans, ...) - new monitor_loop parameter (wait time between queries) - new monitor_info.template - get_power has been renamed to get_powerstate to avoid confusion with Power measures and moved to the Device class as well as get_description - More genericity for the Power and Thermal classes (moved into types.py) - Build process amended (0.4.3 is the next version, repo are generic and in line with pb 0.15 Change-Id: I05016b2557b2f7638e1ea22a89dcf91ebae1066f
This commit is contained in:
parent
929a10a85b
commit
b0ff208138
@ -57,7 +57,7 @@ print("Bios version : {}\n".format(
|
||||
print("Serial Number : {}\n".format(
|
||||
remote_mgmt.Systems.systems_dict["1"].get_serial_number()))
|
||||
print("Power State : {}\n".format(
|
||||
remote_mgmt.Systems.systems_dict["1"].get_power()))
|
||||
remote_mgmt.Systems.systems_dict["1"].get_powerstate()))
|
||||
print("Parameter 'SystemType' : {}\n".format(
|
||||
remote_mgmt.Systems.systems_dict["1"].get_parameter("SystemType")))
|
||||
|
||||
|
@ -115,7 +115,7 @@ vetype:
|
||||
# Global version/tag for the project
|
||||
#
|
||||
projver:
|
||||
python-redfish: 0.4.2
|
||||
python-redfish: 0.4.3
|
||||
projtag:
|
||||
python-redfish: 1
|
||||
#
|
||||
@ -125,22 +125,23 @@ projtag:
|
||||
#
|
||||
# Is it a test version or a production version
|
||||
testver:
|
||||
python-redfish: !!str ""
|
||||
#python-redfish: !!str ""
|
||||
python-redfish: true
|
||||
# Which upper target dir for delivery
|
||||
delivery:
|
||||
python-redfish: !!str ""
|
||||
python-redfish: test
|
||||
#python-redfish: !!str ""
|
||||
#
|
||||
# Additional repository to add at build time
|
||||
# addrepo centos-5-x86_64 = http://packages.sw.be/rpmforge-release/rpmforge-release-0.3.6-1.el5.rf.x86_64.rpm,ftp://ftp.project-builder.org/centos/5/pb.repo
|
||||
# addrepo centos-4-x86_64 = http://packages.sw.be/rpmforge-release/rpmforge-release-0.3.6-1.el4.rf.x86_64.rpm,ftp://ftp.project-builder.org/centos/4/pb.repo
|
||||
# This will allow usage of python-simplejson 1.8.1
|
||||
addrepo:
|
||||
centos-7-x86_64: https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm,ftp://ftp.project-builder.org/centos/7/x86_64/pb.repo
|
||||
mageia-5-x86_64: ftp://ftp.project-builder.org/mageia/5/x86_64/pb.addmedia
|
||||
fedora-25-x86_64: ftp://ftp.project-builder.org/fedora/25/x86_64/pb.repo
|
||||
addbuildrepo:
|
||||
du: ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/python-tortilla.sources.list
|
||||
rpm: ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/python-tortilla.repo
|
||||
md: !!str ""
|
||||
centos: https://dl.fedoraproject.org/pub/epel/epel-release-latest-$pbos->{'version'}.noarch.rpm,ftp://ftp.project-builder.org/$pbos->{'name'}/$pbos->{'version'}/$pbos->{'arch'}/python-tortilla.repo
|
||||
opensuse-42.2-x86_64: ftp://ftp.project-builder.org/opensuse/42.2/x86_64/pb.repo,http://download.opensuse.org/repositories/devel:languages:python3/openSUSE_Leap_42.2/devel:languages:python3.repo,http://download.opensuse.org/repositories/Virtualization:containers/openSUSE_Leap_42.2/Virtualization:containers.repo,http://download.opensuse.org/repositories/devel:languages:python/openSUSE_Leap_42.2/devel:languages:python.repo
|
||||
ubuntu-12.04-x86_64: ftp://ftp.project-builder.org/ubuntu/12.04/python-tortilla.sources.list
|
||||
ubuntu-18.04-x86_64: ftp://ftp.project-builder.org/ubuntu/18.04/python-tortilla.sources.list
|
||||
#
|
||||
# Adapt to your needs:
|
||||
# Optional if you need to overwrite the global values above
|
||||
|
@ -1,2 +1,3 @@
|
||||
[redfish-client]
|
||||
templates_path = PBSHAREPATH/templates
|
||||
monitor_loop = 120
|
||||
|
@ -16,6 +16,7 @@ from builtins import object
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import json
|
||||
import docopt
|
||||
import logging
|
||||
@ -25,6 +26,8 @@ import requests.packages.urllib3
|
||||
import redfish
|
||||
standard_library.install_aliases()
|
||||
|
||||
DEFMONITORLOOP = 120 # Default loop value for monitor command
|
||||
|
||||
|
||||
class InventoryFile(object):
|
||||
'''redfisht-client inventory file management'''
|
||||
@ -252,6 +255,11 @@ if __name__ == '__main__':
|
||||
# information using jinja2 template
|
||||
render_template("serial_info.template")
|
||||
|
||||
def monitor(redfish_data):
|
||||
# Monitor variable values accessible by the manager
|
||||
# information using jinja2 template
|
||||
render_template("monitor_info.template")
|
||||
|
||||
def render_template(template):
|
||||
try:
|
||||
template = jinja2_env.get_template(template)
|
||||
@ -355,6 +363,7 @@ if __name__ == '__main__':
|
||||
|
||||
# Initialize Template system (jinja2)
|
||||
templates_path = config.get("redfish-client", "templates_path")
|
||||
dml = float(config.get("redfish-client", "monitor_loop", DEFMONITORLOOP))
|
||||
logger.debug("Initialize template system")
|
||||
jinja2_env = jinja2.Environment(
|
||||
loader=jinja2.FileSystemLoader(templates_path))
|
||||
@ -401,7 +410,10 @@ if __name__ == '__main__':
|
||||
arguments['<changed_value>'])
|
||||
logger.debug(inventory.data)
|
||||
inventory.save()
|
||||
elif arguments['getinfo'] is True or arguments['getserial'] is True:
|
||||
elif (arguments['getinfo'] is True or
|
||||
arguments['getserial'] is True or
|
||||
arguments['monitor'] is True):
|
||||
|
||||
logger.debug('get commands')
|
||||
# If manager is not defined set it to 'default'
|
||||
if not arguments['<manager_name>']:
|
||||
@ -417,10 +429,9 @@ if __name__ == '__main__':
|
||||
# Could be a nice exercice for learning python. :)
|
||||
logger.info('Gathering data from manager')
|
||||
|
||||
if arguments['--insecure'] is True:
|
||||
redfish_data = get_redfish_data(connection_parameters, False)
|
||||
else:
|
||||
redfish_data = get_redfish_data(connection_parameters, True)
|
||||
redfish_data = get_redfish_data(connection_parameters,
|
||||
not arguments['--insecure'])
|
||||
|
||||
if arguments['getinfo'] is True:
|
||||
if arguments['manager'] is True:
|
||||
logger.debug("Manager commands")
|
||||
@ -434,5 +445,14 @@ if __name__ == '__main__':
|
||||
if arguments['getserial'] is True:
|
||||
logger.debug("serial & part number commands")
|
||||
display_part_serial_numbers(redfish_data)
|
||||
if arguments['monitor'] is True:
|
||||
logger.debug("monitor command")
|
||||
monitor(redfish_data)
|
||||
while True:
|
||||
print("Sleeping for {} seconds".format(dml))
|
||||
time.sleep(dml)
|
||||
redfish_data = get_redfish_data(connection_parameters,
|
||||
not arguments['--insecure'])
|
||||
monitor(redfish_data)
|
||||
logger.info("Client session terminated")
|
||||
sys.exit(0)
|
||||
|
@ -10,6 +10,7 @@ Usage:
|
||||
redfish-client [options] chassis getinfo [<manager_name>]
|
||||
redfish-client [options] system getinfo [<manager_name>]
|
||||
redfish-client [options] getserial [<manager_name>]
|
||||
redfish-client [options] monitor [<manager_name>]
|
||||
redfish-client (-h | --help)
|
||||
redfish-client --version
|
||||
|
||||
@ -25,6 +26,9 @@ Options:
|
||||
--debugfile FILE Specify the client debugfile [default: $HOME/.redfish/redfish-client.log]
|
||||
--libdebugfile FILE Specify python-redfish library log file [default: $HOME/.redfish/python-redfish.log]
|
||||
|
||||
config commands : manage the configuration file.
|
||||
manager commands : manage the manager (Light out management). If <manager_name>
|
||||
is not provided use the 'default' entry
|
||||
Commands:
|
||||
config: manage the configuration file (add, remove modify managers).
|
||||
manager/chassis/system: manage the manager/chassis/system (Light out management). If <manager_name>
|
||||
is not provided use the 'default' entry
|
||||
getserial: display all serial numbers found through that manager to allow inventory
|
||||
monitor: monitor changing variables accessible through that manager in a loop (CTRL-C to exit)
|
||||
|
@ -51,7 +51,7 @@ CPU details :
|
||||
{%- endif %}
|
||||
Available memory : {{ system.get_memory() }}
|
||||
Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }}
|
||||
Power : {{ system.get_power() }}
|
||||
Power : {{ system.get_powerstate() }}
|
||||
Description : {{ system.get_description() }}
|
||||
Chassis : {{ system.get_chassis() | join(', ') }}
|
||||
Managers : {{ system.get_managers() | join(', ') }}
|
||||
@ -88,4 +88,4 @@ Simple Storage :
|
||||
{%- endif %}
|
||||
--------------------------------------------------------------------------------
|
||||
#}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
47
redfish-client/templates/monitor_info.template
Normal file
47
redfish-client/templates/monitor_info.template
Normal file
@ -0,0 +1,47 @@
|
||||
Redfish API version: {{ r.get_api_version() }}
|
||||
{{ r.Root.get_name() }}
|
||||
|
||||
Monitoring
|
||||
==========
|
||||
{% for chassis_index in r.Chassis.chassis_dict | sort %}
|
||||
{%- set chassis = r.Chassis.chassis_dict[chassis_index] %}
|
||||
Chassis #{{ chassis_index }}:
|
||||
Name: {{ chassis.get_name() }}
|
||||
Power: {{ chassis.get_powerstate() }}
|
||||
Temperatures :
|
||||
{%- if chassis.thermal.get_temperatures() == 'Not available' %}
|
||||
Not available
|
||||
{%- else %}
|
||||
{%- for sensor, temp in chassis.thermal.get_temperatures().items() | sort %}
|
||||
{{ sensor }} : {{ temp }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
Fans :
|
||||
{%- if chassis.thermal.get_fans() == 'Not available' %}
|
||||
Not available
|
||||
{%- else %}
|
||||
{%- for fan, rpm in chassis.thermal.get_fans().items() | sort %}
|
||||
{{ fan }} : {{ rpm }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
Power control :
|
||||
{%- if chassis.power.get_power() == 'Not available' %}
|
||||
Not available
|
||||
{%- else %}
|
||||
{%- for ps, volt in chassis.power.get_power().items() | sort %}
|
||||
{{ ps }} : {{ volt }}
|
||||
{%- endfor %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
{% for system_index in r.Systems.systems_dict | sort %}
|
||||
{%- set system = r.Systems.systems_dict[system_index] %}
|
||||
System #{{ system_index }}:
|
||||
Name: {{ system.get_name() }}
|
||||
CPU number: {{ system.get_cpucount() }}
|
||||
CPU model: {{ system.get_cpumodel() }}
|
||||
Status: State: {{ system.get_status().Health }} / Health: {{ system.get_status().Health }}
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
{% endfor %}
|
@ -31,7 +31,7 @@ CPU number: {{ system.get_cpucount() }}
|
||||
CPU model: {{ system.get_cpumodel() }}
|
||||
Available memory: {{ system.get_memory() }} GB
|
||||
Status: State: {{ system.get_status().Health }} / Health: {{ system.get_status().Health }}
|
||||
Power: {{ system.get_power() }}
|
||||
Power: {{ system.get_powerstate() }}
|
||||
{%- if system.data.Oem.Hpe or system.data.Oem.Hp %}
|
||||
{%- if system.network_adapters_collection %}
|
||||
{%- for nic_index in system.network_adapters_collection.network_adapters_dict | sort %}
|
||||
|
@ -29,7 +29,7 @@ CPU details :
|
||||
{%- endif %}
|
||||
Available memory : {{ system.get_memory() }} GB
|
||||
Status : State : {{ system.get_status().Health }} / Health : {{ system.get_status().Health }}
|
||||
Power : {{ system.get_power() }}
|
||||
Power : {{ system.get_powerstate() }}
|
||||
Description : {{ system.get_description() }}
|
||||
Chassis : {{ system.get_chassis() | join(', ') }}
|
||||
Managers : {{ system.get_managers() | join(', ') }}
|
||||
|
@ -322,30 +322,6 @@ class Systems(Device):
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_power(self):
|
||||
'''Get power status of the system.
|
||||
|
||||
:returns: system power state or "Not available"
|
||||
:rtype: string
|
||||
|
||||
'''
|
||||
try:
|
||||
return self.data.PowerState
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_description(self):
|
||||
'''Get description of the system.
|
||||
|
||||
:returns: system description or "Not available"
|
||||
:rtype: string
|
||||
|
||||
'''
|
||||
try:
|
||||
return self.data.Description
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_cpucount(self):
|
||||
'''Get the number of cpu in the system.
|
||||
|
||||
@ -697,21 +673,6 @@ class ChassisCollection(BaseCollection):
|
||||
|
||||
class Chassis(Device):
|
||||
'''Class to manage redfish Chassis data.'''
|
||||
def __init__(self, url, connection_parameters):
|
||||
'''Class constructor'''
|
||||
super(Chassis, self).__init__(url, connection_parameters)
|
||||
|
||||
try:
|
||||
self.thermal = Thermal(self.get_link_url('Thermal'),
|
||||
connection_parameters)
|
||||
except AttributeError:
|
||||
self.thermal = None
|
||||
|
||||
try:
|
||||
self.power = Power(self.get_link_url('Power'),
|
||||
connection_parameters)
|
||||
except AttributeError:
|
||||
self.Power = None
|
||||
|
||||
def get_type(self):
|
||||
'''Get chassis type
|
||||
@ -724,43 +685,3 @@ class Chassis(Device):
|
||||
return self.data.ChassisType
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
|
||||
class Thermal(Base):
|
||||
'''Class to manage redfish Thermal data.'''
|
||||
def get_temperatures(self):
|
||||
'''Get chassis sensors name and temparature
|
||||
|
||||
:returns: chassis sensor and temperature
|
||||
:rtype: dict
|
||||
|
||||
'''
|
||||
temperatures = {}
|
||||
|
||||
try:
|
||||
for sensor in self.data.Temperatures:
|
||||
temperatures[sensor.Name] = sensor.ReadingCelsius
|
||||
return temperatures
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_fans(self):
|
||||
'''Get chassis fan name and rpm
|
||||
|
||||
:returns: chassis fan and rpm
|
||||
:rtype: dict
|
||||
|
||||
'''
|
||||
fans = {}
|
||||
|
||||
try:
|
||||
for fan in self.data.Fans:
|
||||
fans[fan.FanName] = fan.ReadingRPM
|
||||
return fans
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
|
||||
class Power(Base):
|
||||
'''Class to manage redfish Power data.'''
|
||||
pass
|
||||
|
112
redfish/types.py
112
redfish/types.py
@ -15,6 +15,7 @@ import ssl
|
||||
from . import config
|
||||
from . import mapping
|
||||
from . import exception
|
||||
|
||||
standard_library.install_aliases()
|
||||
|
||||
|
||||
@ -172,10 +173,97 @@ class BaseCollection(Base):
|
||||
config.logger.debug(self.links)
|
||||
|
||||
|
||||
class Thermal(Base):
|
||||
'''Class to manage redfish Thermal data.'''
|
||||
|
||||
# Currently doesn't support case such as:
|
||||
# /redfish/v1/Systems/1/SmartStorage/ArrayControllers/0/DiskDrives/1/
|
||||
# u'CurrentTemperatureCelsius': 45
|
||||
def get_temperatures(self):
|
||||
'''Get chassis sensors name and temperature
|
||||
|
||||
:returns: chassis sensor and temperature
|
||||
:rtype: dict
|
||||
|
||||
'''
|
||||
temperatures = {}
|
||||
|
||||
try:
|
||||
for sensor in self.data.Temperatures:
|
||||
temperatures[sensor.Name] = sensor.ReadingCelsius
|
||||
return temperatures
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_fans(self):
|
||||
'''Get chassis fan name and rpm
|
||||
|
||||
:returns: chassis fan and rpm
|
||||
:rtype: dict
|
||||
|
||||
'''
|
||||
fans = {}
|
||||
|
||||
try:
|
||||
for fan in self.data.Fans:
|
||||
# New interface
|
||||
fans[fan.Name] = str(fan.Reading) + ' ' + fan.ReadingUnits
|
||||
return fans
|
||||
except AttributeError:
|
||||
try:
|
||||
# Old interface
|
||||
for fan in self.data.Fans:
|
||||
fans[fan.FanName] = fan.ReadingRPM
|
||||
|
||||
return fans
|
||||
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
|
||||
class Power(Base):
|
||||
'''Class to manage redfish Power data.'''
|
||||
|
||||
def get_power(self):
|
||||
'''Get power supply informartion
|
||||
|
||||
:returns: power supply and Voltage
|
||||
:rtype: dict
|
||||
|
||||
'''
|
||||
pc = {}
|
||||
|
||||
# config.logger.debug("PowerControl parsed")
|
||||
# config.logger.debug(self.data)
|
||||
try:
|
||||
for p in self.data.PowerControl:
|
||||
pc[p.MemberId] = str(p.PowerConsumedWatts) + ' Watts'
|
||||
return pc
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
|
||||
class Device(Base):
|
||||
'''Abstract class to add common methods between devices
|
||||
(Chassis, Servers, System).
|
||||
'''
|
||||
|
||||
def __init__(self, url, connection_parameters):
|
||||
'''Class constructor'''
|
||||
super(Device, self).__init__(url, connection_parameters)
|
||||
|
||||
try:
|
||||
url2 = self.get_link_url('Thermal')
|
||||
self.thermal = Thermal(url2, connection_parameters)
|
||||
except AttributeError:
|
||||
self.thermal = Thermal(url, connection_parameters)
|
||||
|
||||
try:
|
||||
url2 = self.get_link_url('Power')
|
||||
self.power = Power(url2, connection_parameters)
|
||||
except AttributeError:
|
||||
self.power = Power(url, connection_parameters)
|
||||
|
||||
def get_uuid(self):
|
||||
'''Get device uuid
|
||||
|
||||
@ -287,6 +375,30 @@ class Device(Base):
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_description(self):
|
||||
'''Get description of the device.
|
||||
|
||||
:returns: device description or "Not available"
|
||||
:rtype: string
|
||||
|
||||
'''
|
||||
try:
|
||||
return self.data.Description
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_powerstate(self):
|
||||
'''Get power status of the device.
|
||||
|
||||
:returns: device power state or "Not available"
|
||||
:rtype: string
|
||||
|
||||
'''
|
||||
try:
|
||||
return self.data.PowerState
|
||||
except AttributeError:
|
||||
return "Not available"
|
||||
|
||||
def get_fw_version(self):
|
||||
'''Get firmware version of the device.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user