mirror of
				https://github.com/xcat2/xcat-core.git
				synced 2025-10-24 16:05:41 +00:00 
			
		
		
		
	rbeacon & rvitals for openbmc in python (#4795)
* rbeacon & rvitals for openbmc in python
This commit is contained in:
		| @@ -7,6 +7,7 @@ | ||||
| import struct | ||||
| import sys | ||||
| import inspect | ||||
| import re | ||||
| import logging | ||||
| from logging.handlers import SysLogHandler | ||||
|  | ||||
| @@ -70,6 +71,17 @@ def update2Ddict(updata_dict, key_a, key_b, value): | ||||
|     else:  | ||||
|         updata_dict.update({key_a: {key_b: value}}) | ||||
|  | ||||
| def emb_numbers(string): | ||||
|     re_digits = re.compile(r'(\d+)') | ||||
|     pieces = re_digits.split(string) | ||||
|     pieces[1::2] = map(int,pieces[1::2]) | ||||
|     return pieces | ||||
|  | ||||
| def sort_string_with_numbers(origin_list): | ||||
|     new_list = [(emb_numbers(string),string) for string in origin_list] | ||||
|     new_list.sort() | ||||
|     return [string for __,string in new_list] | ||||
|  | ||||
| class Messager(object): | ||||
|     def __init__(self, name=None): | ||||
|         self.logger = logging.getLogger(name or 'xcatagent') | ||||
|   | ||||
							
								
								
									
										26
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/beacon.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/beacon.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| class BeaconInterface(object): | ||||
|     """Interface for beacon-related actions.""" | ||||
|     interface_type = 'beacon' | ||||
|     version = '1.0' | ||||
|  | ||||
|     def set_beacon_state(self, task, beacon_state, timeout=None): | ||||
|         """Set the beacon state of the task's nodes. | ||||
|  | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :param beacon_state: on|off beacon state. | ||||
|         :param timeout: timeout (in seconds) positive integer (> 0) for any | ||||
|           beacon state. ``None`` indicates to use default timeout. | ||||
|         """ | ||||
|         return task.run('set_state', beacon_state, timeout=timeout) | ||||
|  | ||||
|  | ||||
| class DefaultBeaconManager(BeaconInterface): | ||||
|     """Interface for beacon-related actions.""" | ||||
|     pass | ||||
| @@ -0,0 +1,35 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
| from __future__ import print_function | ||||
| import gevent | ||||
| import time | ||||
|  | ||||
| from common.task import ParallelNodesCommand | ||||
| from common.exceptions import SelfClientException, SelfServerException | ||||
| from hwctl import openbmc_client as openbmc | ||||
|  | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
|  | ||||
|  | ||||
| class OpenBMCBeaconTask(ParallelNodesCommand): | ||||
|     """Executor for beacon-related actions.""" | ||||
|  | ||||
|     def set_state(self, state, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|         try: | ||||
|             obmc.login() | ||||
|             obmc.set_beacon_state(state) | ||||
|             result = '%s: %s' % (node, state) | ||||
|  | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             result = '%s: %s'  % (node, e.message) | ||||
|  | ||||
|         self.callback.info(result) | ||||
| @@ -0,0 +1,118 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| from __future__ import print_function | ||||
| import gevent | ||||
| import time | ||||
|  | ||||
| from common.task import ParallelNodesCommand | ||||
| from common.exceptions import SelfClientException, SelfServerException | ||||
| from hwctl import openbmc_client as openbmc | ||||
| from common import utils | ||||
|  | ||||
| import logging | ||||
| logger = logging.getLogger('xcatagent') | ||||
|  | ||||
|  | ||||
| SENSOR_TYPE_UNIT = { | ||||
|     "altitude" : "Meters", | ||||
|     "fanspeed" : "RPMS", | ||||
|     "temp"     : "DegreesC", | ||||
|     "voltage"  : "Volts", | ||||
|     "wattage"  : "Watts", | ||||
| } | ||||
|  | ||||
| SENSOR_POWER_UNITS = ("Amperes", "Joules", "Watts") | ||||
|  | ||||
|  | ||||
| class OpenBMCSensorTask(ParallelNodesCommand): | ||||
|     """Executor for sensor-related actions.""" | ||||
|  | ||||
|     def _get_beacon_info(self, beacon_dict): | ||||
|  | ||||
|         info_list = [] | ||||
|         info_list.append('Front . . . . . : Power:%s Fault:%s Identify:%s' % | ||||
|                          (beacon_dict.get('front_power', 'N/A'), | ||||
|                           beacon_dict.get('front_fault', 'N/A'), | ||||
|                           beacon_dict.get('front_id', 'N/A'))) | ||||
|         if (beacon_dict.get('fan0', 'N/A') == 'Off' and beacon_dict.get('fan1', 'N/A') == 'Off' and | ||||
|             beacon_dict.get('fan2', 'N/A') == 'Off' and beacon_dict.get('fan3', 'N/A') == 'Off'): | ||||
|             info_list.append('Front Fans  . . : No LEDs On') | ||||
|         else: | ||||
|             info_list.append('Front Fans  . . : fan0:%s fan1:%s fan2:%s fan3:%s' % | ||||
|                              (beacon_dict.get('fan0', 'N/A'), beacon_dict.get('fan1', 'N/A'), | ||||
|                               beacon_dict.get('fan2', 'N/A'), beacon_dict.get('fan3', 'N/A'))) | ||||
|         info_list.append('Rear  . . . . . : Power:%s Fault:%s Identify:%s' % | ||||
|                          (beacon_dict.get('rear_power', 'N/A'), | ||||
|                           beacon_dict.get('rear_fault', 'N/A'), | ||||
|                           beacon_dict.get('rear_id', 'N/A'))) | ||||
|         return info_list | ||||
|          | ||||
|  | ||||
|     def get_sensor_info(self, sensor_type, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|  | ||||
|         sensor_info = [] | ||||
|         try: | ||||
|             obmc.login() | ||||
|             sensor_info_dict = obmc.get_sensor_info() | ||||
|  | ||||
|             if sensor_type == 'all' or not sensor_type: | ||||
|                 for sensor_key in sensor_info_dict: | ||||
|                     sensor_info += sensor_info_dict[sensor_key] | ||||
|                 sensor_info = utils.sort_string_with_numbers(sensor_info) | ||||
|                 beacon_dict = obmc.get_beacon_info() | ||||
|                 sensor_info += self._get_beacon_info(beacon_dict) | ||||
|             elif sensor_type == 'power': | ||||
|                 for sensor_key in sensor_info_dict: | ||||
|                     if sensor_key in SENSOR_POWER_UNITS: | ||||
|                         sensor_info += sensor_info_dict[sensor_key] | ||||
|                 sensor_info = utils.sort_string_with_numbers(sensor_info) | ||||
|             else: | ||||
|                 sensor_unit = SENSOR_TYPE_UNIT[sensor_type] | ||||
|                 if sensor_unit in sensor_info_dict: | ||||
|                     sensor_info += sensor_info_dict[sensor_unit] | ||||
|                     sensor_info = utils.sort_string_with_numbers(sensor_info) | ||||
|  | ||||
|             if not sensor_info: | ||||
|                 sensor_info = ['No attributes returned from the BMC.'] | ||||
|  | ||||
|             for info in sensor_info: | ||||
|                 self.callback.info( '%s: %s' % (node, info)) | ||||
|  | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             self.callback.info('%s: %s'  % (node, e.message)) | ||||
|  | ||||
|         return sensor_info | ||||
|  | ||||
|     def get_beacon_info(self, **kw): | ||||
|  | ||||
|         node = kw['node'] | ||||
|         obmc = openbmc.OpenBMCRest(name=node, nodeinfo=kw['nodeinfo'], messager=self.callback, | ||||
|                                    debugmode=self.debugmode, verbose=self.verbose) | ||||
|  | ||||
|         beacon_info = [] | ||||
|         try: | ||||
|             obmc.login() | ||||
|             beacon_dict = obmc.get_beacon_info() | ||||
|             beacon_info = self._get_beacon_info(beacon_dict) | ||||
|  | ||||
|             if not beacon_info: | ||||
|                 beacon_info = ['No attributes returned from the BMC.'] | ||||
|  | ||||
|             for info in beacon_info: | ||||
|                 self.callback.info( '%s: %s' % (node, info)) | ||||
|  | ||||
|         except (SelfServerException, SelfClientException) as e: | ||||
|             self.callback.info('%s: %s'  % (node, e.message)) | ||||
|  | ||||
|         return beacon_info | ||||
|                  | ||||
|  | ||||
| @@ -19,6 +19,34 @@ HTTP_PROTOCOL = "https://" | ||||
| PROJECT_URL = "/xyz/openbmc_project" | ||||
| PROJECT_PAYLOAD = "xyz.openbmc_project." | ||||
|  | ||||
| RBEACON_URLS = { | ||||
|     "path"      : "/led/groups/enclosure_identify/attr/Asserted", | ||||
|     "on"        : { | ||||
|         "field" : True, | ||||
|     }, | ||||
|     "off"        : { | ||||
|         "field" : False, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| LEDS_URL = "/led/physical/enumerate" | ||||
|  | ||||
| LEDS_KEY_LIST = ("fan0", "fan1", "fan2", "fan3", | ||||
|                  "front_id", "front_fault", "front_power", | ||||
|                  "rear_id", "rear_fault", "rear_power") | ||||
|  | ||||
| SENSOR_URL = "/sensors/enumerate" | ||||
|  | ||||
| SENSOR_UNITS = { | ||||
|     "Amperes"  : "Amps", | ||||
|     "DegreesC" : "C", | ||||
|     "Joules"   : "Joules", | ||||
|     "Meters"   : "Meters", | ||||
|     "RPMS"     : "RPMS", | ||||
|     "Volts"    : "Volts", | ||||
|     "Watts"    : "Watts", | ||||
| } | ||||
|  | ||||
| RPOWER_STATES = { | ||||
|     "on"        : "on", | ||||
|     "off"       : "off", | ||||
| @@ -308,6 +336,52 @@ class OpenBMCRest(object): | ||||
|             error = 'Error: Received wrong format response: %s' % states | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def get_beacon_info(self): | ||||
|  | ||||
|         beacon_data = self.request('GET', LEDS_URL, cmd='get_beacon_info')  | ||||
|         try: | ||||
|             beacon_dict = {} | ||||
|             for key, value in beacon_data.items(): | ||||
|                 key_id = key.split('/')[-1] | ||||
|                 if key_id in LEDS_KEY_LIST: | ||||
|                     beacon_dict[key_id] = value['State'].split('.')[-1] | ||||
|             return beacon_dict | ||||
|         except KeyError: | ||||
|             error = 'Error: Received wrong format response: %s' % beacon_data | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def set_beacon_state(self, state): | ||||
|  | ||||
|         payload = { "data": RBEACON_URLS[state]['field'] } | ||||
|         self.request('PUT', RBEACON_URLS['path'], payload=payload, cmd='set_beacon_state') | ||||
|  | ||||
|     def get_sensor_info(self): | ||||
|  | ||||
|         sensor_data = self.request('GET', SENSOR_URL, cmd='get_sensor_info') | ||||
|         try: | ||||
|             sensor_dict = {} | ||||
|             for k, v in sensor_data.items(): | ||||
|                 if 'Unit' in v: | ||||
|                     unit = v['Unit'].split('.')[-1] | ||||
|                     if unit in SENSOR_UNITS: | ||||
|                         label = k.split('/')[-1].replace('_', ' ').title() | ||||
|                         value = v['Value'] | ||||
|                         scale = v['Scale'] | ||||
|                         value = value * pow(10, scale) | ||||
|                         value = '{:g}'.format(value) | ||||
|                         if unit not in sensor_dict: | ||||
|                             sensor_dict[unit] = [] | ||||
|                         sensor_dict[unit].append('%s: %s %s' % (label, value, SENSOR_UNITS[unit])) | ||||
|                 elif 'units' in v and 'value' in v: | ||||
|                     label = k.split('/')[-1] | ||||
|                     value = v['value'] | ||||
|                     sensor_dict[label] = ['%s: %s' % (label, value)] | ||||
|                      | ||||
|             return sensor_dict | ||||
|         except KeyError: | ||||
|             error = 'Error: Received wrong format response: %s' % sensor_data | ||||
|             raise SelfServerException(error) | ||||
|  | ||||
|     def list_firmware(self): | ||||
|  | ||||
|         data = self.request('GET', FIRM_URLS['list']['path'], cmd='list_firmware') | ||||
| @@ -347,3 +421,4 @@ class OpenBMCImage(object): | ||||
|  | ||||
|     def __str__(self): | ||||
|         return '%s-%s' % (self.purpose, self.id) | ||||
|  | ||||
|   | ||||
							
								
								
									
										32
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								xCAT-openbmc-py/lib/python/agent/hwctl/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| #!/usr/bin/env python | ||||
| ############################################################################### | ||||
| # IBM(c) 2018 EPL license http://www.eclipse.org/legal/epl-v10.html | ||||
| ############################################################################### | ||||
| # -*- coding: utf-8 -*- | ||||
| # | ||||
|  | ||||
| class SensorInterface(object): | ||||
|     """Interface for sensor-related actions.""" | ||||
|     interface_type = 'sensor' | ||||
|     version = '1.0' | ||||
|  | ||||
|     def get_sensor_info(self, task, sensor_type=None): | ||||
|         """Return the sensor info of the task's nodes. | ||||
|  | ||||
|         :param sensor_type: type of sensor info want to get. | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :return: sensor info list | ||||
|         """ | ||||
|         return task.run('get_sensor_info', sensor_type)  | ||||
|  | ||||
|     def get_beacon_info(self, task): | ||||
|         """Return the beacon info of the task's nodes. | ||||
|  | ||||
|         :param task: a Task instance containing the nodes to act on. | ||||
|         :return: beacon info list | ||||
|         """ | ||||
|         return task.run('get_beacon_info') | ||||
|  | ||||
| class DefaultSensorManager(SensorInterface): | ||||
|     """Interface for sensor-related actions.""" | ||||
|     pass | ||||
| @@ -13,10 +13,14 @@ from docopt import docopt | ||||
|  | ||||
| from common import utils | ||||
| from common import exceptions as xcat_exception | ||||
| from hwctl.executor.openbmc_beacon import OpenBMCBeaconTask | ||||
| from hwctl.executor.openbmc_power import OpenBMCPowerTask | ||||
| from hwctl.executor.openbmc_setboot import OpenBMCBootTask | ||||
| from hwctl.executor.openbmc_sensor import OpenBMCSensorTask | ||||
| from hwctl.beacon import DefaultBeaconManager | ||||
| from hwctl.power import DefaultPowerManager | ||||
| from hwctl.setboot import DefaultBootManager | ||||
| from hwctl.sensor import DefaultSensorManager | ||||
|  | ||||
| from xcatagent import base | ||||
| import openbmc_rest | ||||
| @@ -36,6 +40,9 @@ VERBOSE = False | ||||
|  | ||||
| all_nodes_result = {} | ||||
|  | ||||
| # global variables of rbeacon | ||||
| BEACON_SET_OPTIONS = ('on', 'off') | ||||
|  | ||||
| # global variables of rflash | ||||
| RFLASH_OPTIONS = { | ||||
|     "-a"         : "activate", | ||||
| @@ -83,6 +90,10 @@ POWER_GET_OPTIONS = ('bmcstate', 'state', 'stat', 'status') | ||||
| SETBOOT_GET_OPTIONS = ('stat', '') | ||||
| SETBOOT_SET_OPTIONS = ('cd', 'def', 'default', 'hd', 'net') | ||||
|  | ||||
| # global variables of rvitals | ||||
| VITALS_OPTIONS = ('all', 'altitude', 'fanspeed', 'leds', 'power', | ||||
|                   'temp', 'voltage', 'wattage') | ||||
|  | ||||
| class OpenBMC(base.BaseDriver): | ||||
|  | ||||
|     headers = {'Content-Type': 'application/json'} | ||||
| @@ -541,6 +552,39 @@ class OpenBMCManager(base.BaseManager): | ||||
|         if self.debugmode: | ||||
|             logger.setLevel(logging.DEBUG) | ||||
|  | ||||
|     def rbeacon(self, nodesinfo, args): | ||||
|  | ||||
|         # 1, parse args | ||||
|         rbeacon_usage = """ | ||||
|         Usage: | ||||
|             rbeacon [-V|--verbose] [on|off] | ||||
|  | ||||
|         Options: | ||||
|             -V --verbose   rbeacon verbose mode. | ||||
|         """ | ||||
|  | ||||
|         try: | ||||
|             opts = docopt(rbeacon_usage, argv=args) | ||||
|  | ||||
|             self.verbose = opts.pop('--verbose') | ||||
|             action = [k for k,v in opts.items() if v][0] | ||||
|         except Exception as e: | ||||
|             self.messager.error("Failed to parse arguments for rbeacon: %s" % args) | ||||
|             return | ||||
|  | ||||
|         # 2, validate the args | ||||
|         if action is None: | ||||
|             self.messager.error("Not specify the subcommand for rbeacon") | ||||
|             return | ||||
|  | ||||
|         if action not in BEACON_SET_OPTIONS: | ||||
|             self.messager.error("Not supported subcommand for rbeacon: %s" % action) | ||||
|             return | ||||
|  | ||||
|         # 3, run the subcommands | ||||
|         runner = OpenBMCBeaconTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose) | ||||
|         DefaultBeaconManager().set_beacon_state(runner, beacon_state=action) | ||||
|  | ||||
|     def rpower(self, nodesinfo, args): | ||||
|  | ||||
|         # 1, parse args | ||||
| @@ -617,6 +661,41 @@ class OpenBMCManager(base.BaseManager): | ||||
|         else: | ||||
|             DefaultBootManager().set_boot_state(runner, setboot_state=action, persistant=action_type) | ||||
|  | ||||
|     def rvitals(self, nodesinfo, args): | ||||
|  | ||||
|         # 1, parse agrs | ||||
|         if not args: | ||||
|             args = ['all'] | ||||
|  | ||||
|         rvitals_usage = """ | ||||
|         Usage: | ||||
|             rvitals [-V|--verbose] [all|altitude|fanspeed|leds|power|temp|voltage|wattage] | ||||
|  | ||||
|         Options: | ||||
|             -V --verbose   rvitals verbose mode. | ||||
|         """ | ||||
|  | ||||
|         try: | ||||
|             opts = docopt(rvitals_usage, argv=args) | ||||
|  | ||||
|             self.verbose = opts.pop('--verbose') | ||||
|             action = [k for k,v in opts.items() if v][0] | ||||
|         except Exception as e: | ||||
|             self.messager.error("Failed to parse arguments for rvitals: %s" % args) | ||||
|             return | ||||
|  | ||||
|         # 2, validate the args | ||||
|         if action not in VITALS_OPTIONS: | ||||
|             self.messager.error("Not supported subcommand for rvitals: %s" % action) | ||||
|             return | ||||
|  | ||||
|         # 3, run the subcommands | ||||
|         runner = OpenBMCSensorTask(nodesinfo, callback=self.messager, debugmode=self.debugmode, verbose=self.verbose) | ||||
|         if action == 'leds': | ||||
|             DefaultSensorManager().get_beacon_info(runner) | ||||
|         else: | ||||
|             DefaultSensorManager().get_sensor_info(runner, action) | ||||
|  | ||||
|  | ||||
|     def _get_full_path(self,file_path): | ||||
|         if type(self.cwd) == 'unicode': | ||||
|   | ||||
| @@ -30,8 +30,11 @@ use xCAT::OPENBMC; | ||||
|  | ||||
| sub handled_commands { | ||||
|     return { | ||||
|         rbeacon        => 'nodehm:mgt=openbmc', | ||||
|         rflash         => 'nodehm:mgt=openbmc', | ||||
|         rpower         => 'nodehm:mgt=openbmc', | ||||
|         rsetboot       => 'nodehm:mgt=openbmc', | ||||
|         rvitals        => 'nodehm:mgt=openbmc', | ||||
|     }; | ||||
| } | ||||
|  | ||||
| @@ -144,15 +147,19 @@ sub parse_args { | ||||
|         return ([ 1, "Error parsing arguments." ]); | ||||
|     } | ||||
|  | ||||
|     if (scalar(@ARGV) >= 2 and ($command =~ /rpower/)) { | ||||
|     if (scalar(@ARGV) >= 2 and ($command =~ /rbeacon|rpower|rvitals/)) { | ||||
|         return ([ 1, "Only one option is supported at the same time for $command" ]); | ||||
|     } elsif (scalar(@ARGV) == 0 and $command =~ /rpower|rflash/) { | ||||
|     } elsif (scalar(@ARGV) == 0 and $command =~ /rbeacon|rpower|rflash/) { | ||||
|         return ([ 1, "No option specified for $command" ]); | ||||
|     } else { | ||||
|         $subcommand = $ARGV[0]; | ||||
|     } | ||||
|  | ||||
|     if ($command eq "rflash") { | ||||
|     if ($command eq "rbeacon") { | ||||
|         unless ($subcommand =~ /^on$|^off$/) { | ||||
|             return ([ 1, "Only 'on' or 'off' is supported for OpenBMC managed nodes."]); | ||||
|         } | ||||
|     } elsif ($command eq "rflash") { | ||||
|         my ($activate, $check, $delete, $directory, $list, $upload) = (0) x 6; | ||||
|         my $no_host_reboot; | ||||
|         GetOptions( | ||||
| @@ -173,7 +180,7 @@ sub parse_args { | ||||
|                     return ([ 1, "Unsupported command: $command $arg" ]); | ||||
|                 } | ||||
|             } | ||||
|             return ([ 1, "No options specified."]); | ||||
|             return ([ 1, "No options specified." ]); | ||||
|         } | ||||
|         if ($activate or $check or $delete or $upload) { | ||||
|             return ([ 1, "More than one firmware specified is not supported."]) if ($#ARGV >= 1); | ||||
| @@ -213,6 +220,11 @@ sub parse_args { | ||||
|         unless ($subcommand =~ /^net$|^hd$|^cd$|^def$|^default$|^stat$/) { | ||||
|             return ([ 1, "Unsupported command: $command $subcommand" ]); | ||||
|         } | ||||
|     } elsif ($command eq "rvitals") { | ||||
|         $subcommand = "all" if (!defined($ARGV[0])); | ||||
|         unless ($subcommand =~ /^all$|^altitude$|^fanspeed$|^leds$|^power$|^temp$|^voltage$|^wattage$/) { | ||||
|             return ([ 1, "Unsupported command: $command $subcommand" ]); | ||||
|         } | ||||
|     } else { | ||||
|         return ([ 1, "Unsupported command: $command" ]); | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user