| #!/usr/bin/python -u |
| |
| import gobject |
| import dbus |
| import dbus.service |
| import dbus.mainloop.glib |
| import os |
| import os.path as path |
| import sys |
| from obmc.dbuslib.bindings import DbusProperties, get_dbus |
| from IPy import IP |
| |
| settings_file_path = os.path.join( |
| sys.prefix, |
| 'share', |
| 'phosphor-settings') |
| sys.path.insert(1, settings_file_path) |
| import settings_file as s |
| import re |
| |
| DBUS_NAME = 'org.openbmc.settings.Host' |
| OBJ_NAME = '/org/openbmc/settings/host0' |
| CONTROL_INTF = 'org.openbmc.Settings' |
| |
| |
| class HostSettingsObject(DbusProperties): |
| def __init__(self, bus, name, settings, path): |
| super(HostSettingsObject, self).__init__( |
| conn=bus, |
| object_path=name, |
| validator=self.input_validator) |
| self.bus = bus |
| self.path = path |
| # Needed to ignore the validation on default networkconfig values as |
| # opposed to user giving the same. |
| self.adminmode = True |
| |
| if not os.path.exists(path): |
| os.mkdir(path) |
| |
| # Listen to changes in the property values and sync them to the BMC |
| bus.add_signal_receiver( |
| self.settings_signal_handler, |
| dbus_interface="org.freedesktop.DBus.Properties", |
| signal_name="PropertiesChanged", |
| path="/org/openbmc/settings/host0") |
| |
| # Create the dbus properties |
| for i in settings[DBUS_NAME].iterkeys(): |
| shk = settings[DBUS_NAME][i] |
| self.set_settings_property(shk['name'], |
| shk['type'], |
| shk['default']) |
| # Done with consuming factory settings. |
| self.adminmode = False |
| |
| def get_bmc_value(self, name): |
| try: |
| with open(path.join(self.path, name), 'r') as f: |
| return f.read() |
| except (IOError): |
| pass |
| return None |
| |
| # Create dbus properties based on bmc value. |
| # This will be either a value previously set, |
| # or the default file value if the BMC value |
| # does not exist. |
| def set_settings_property(self, name, type, value): |
| bmcv = self.get_bmc_value(name) |
| if bmcv: |
| value = bmcv |
| if type == "i": |
| self.Set(DBUS_NAME, name, int(value)) |
| elif type == "s": |
| self.Set(DBUS_NAME, name, str(value)) |
| elif type == "b": |
| self.Set(DBUS_NAME, name, bool(value)) |
| |
| # Save the settings to the BMC. This will write the settings value in |
| # individual files named by the property name to the BMC. |
| def set_system_settings(self, name, value): |
| bmcv = self.get_bmc_value(name) |
| if bmcv != value: |
| filepath = path.join(self.path, name) |
| with open(filepath, 'w') as f: |
| f.write(str(value)) |
| |
| # Signal handler for when one ore more settings properties were updated. |
| # This will sync the changes to the BMC. |
| def settings_signal_handler( |
| self, interface_name, changed_properties, invalidated_properties): |
| for name, value in changed_properties.items(): |
| self.set_system_settings(name, value) |
| |
| # Placeholder signal. Needed to register the settings interface. |
| @dbus.service.signal(DBUS_NAME, signature='s') |
| def SettingsUpdated(self, sname): |
| pass |
| |
| def validate_regex(self, regex, value): |
| if not re.compile(regex).search(value): |
| raise ValueError("Invalid input. Data does not satisfy regex") |
| |
| def validate_range(self, min, max, value): |
| if value not in range(min, max): |
| raise ValueError("Invalid input. Data not in allowed range") |
| |
| def validate_list_ignore_case(self, lst, value): |
| if value.lower() not in map(lambda val: val.lower(), lst): |
| raise ValueError("Invalid input. Data not in allowed values") |
| |
| # validate host network configuration |
| # need "ipaddress=,prefix=,gateway=,mac=,addr_type=" |
| # Must be able to handle any order |
| def validate_net_config(self, value): |
| if self.adminmode: |
| return |
| |
| # Need all of these to be given by the user. |
| user_config = [] |
| all_config = ['ipaddress', 'prefix', 'gateway', 'mac', 'addr_type'] |
| |
| # This has a hard data format mentioned above so no blanks allowed. |
| if value.count(" ") or value.count("=") != 5: |
| raise ValueError("Invalid Network Data. No white spaces allowed") |
| |
| config = value.split(',') |
| for key_value in config: |
| key, value = key_value.split('=') |
| if not key or not value: |
| raise ValueError("Invalid key or Data") |
| |
| # Add the current key seen so we can compare at the end to see |
| # if all values have been given |
| user_config.append(key.lower()) |
| |
| if key.lower() == 'ipaddress' or key.lower() == 'gateway': |
| IP(value) |
| |
| elif key.lower() == 'mac': |
| regex = '([a-fA-F0-9]{2}[:|\-]?){6}' |
| self.validate_regex(regex, value) |
| |
| elif key.lower() == 'prefix': |
| self.validate_range(0, 33, int(value)) |
| |
| elif key.lower() == 'addr_type': |
| allowed = ["STATIC", "DYNAMIC"] |
| self.validate_list_ignore_case(allowed, value) |
| |
| # Did user pass everything ?? |
| if set(all_config) - set(user_config): |
| raise ValueError( |
| "Invalid Network Data. All information is mandatory") |
| |
| # Validate to see if the changes are in order |
| def input_validator(self, iface, proprty, value): |
| settings = s.SETTINGS |
| shk = {} |
| for key in settings[iface].iterkeys(): |
| if proprty == settings[iface][key]['name']: |
| shk = settings[iface][key] |
| break |
| |
| # User entered key is not present |
| if not shk: |
| raise KeyError("Invalid Property") |
| |
| if shk['validation'] == 'list': |
| self.validate_list_ignore_case(shk['allowed'], value) |
| |
| elif shk['validation'] == 'range': |
| self.validate_range(shk['min'], shk['max']+1, value) |
| |
| elif shk['validation'] == 'regex': |
| self.validate_regex(shk['regex'], value) |
| |
| elif shk['validation'] == 'custom': |
| getattr(self, shk['method'])(value) |
| |
| if __name__ == '__main__': |
| dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
| |
| bus = get_dbus() |
| obj = HostSettingsObject(bus, OBJ_NAME, s.SETTINGS, "/var/lib/obmc/") |
| mainloop = gobject.MainLoop() |
| |
| obj.unmask_signals() |
| name = dbus.service.BusName(DBUS_NAME, bus) |
| print "Running HostSettingsService" |
| mainloop.run() |
| |
| # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 |