Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 1 | #!/usr/bin/python -u |
| 2 | |
| 3 | import gobject |
| 4 | import dbus |
| 5 | import dbus.service |
| 6 | import dbus.mainloop.glib |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 7 | import os |
| 8 | import os.path as path |
Adriana Kobylak | 256be78 | 2016-08-24 15:43:16 -0500 | [diff] [blame] | 9 | import sys |
Brad Bishop | 5ef7fe6 | 2016-05-17 08:39:17 -0400 | [diff] [blame] | 10 | from obmc.dbuslib.bindings import DbusProperties, get_dbus |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 11 | from IPy import IP |
Adriana Kobylak | 256be78 | 2016-08-24 15:43:16 -0500 | [diff] [blame] | 12 | |
| 13 | settings_file_path = os.path.join(sys.prefix, 'share/obmc-phosphor-settings') |
| 14 | sys.path.insert(1, settings_file_path) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 15 | import settings_file as s |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 16 | import re |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 17 | |
| 18 | DBUS_NAME = 'org.openbmc.settings.Host' |
| 19 | OBJ_NAME = '/org/openbmc/settings/host0' |
| 20 | CONTROL_INTF = 'org.openbmc.Settings' |
| 21 | |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 22 | |
Brad Bishop | 5ef7fe6 | 2016-05-17 08:39:17 -0400 | [diff] [blame] | 23 | class HostSettingsObject(DbusProperties): |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 24 | def __init__(self, bus, name, settings, path): |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 25 | super(HostSettingsObject, self).__init__(conn=bus, object_path=name, |
| 26 | validator=self.input_validator) |
| 27 | self.bus = bus |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 28 | self.path = path |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 29 | # Needed to ignore the validation on default networkconfig values as |
| 30 | # opposed to user giving the same. |
| 31 | self.adminmode = True |
| 32 | |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 33 | if not os.path.exists(path): |
| 34 | os.mkdir(path) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 35 | |
| 36 | # Listen to changes in the property values and sync them to the BMC |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 37 | bus.add_signal_receiver( |
| 38 | self.settings_signal_handler, |
| 39 | dbus_interface="org.freedesktop.DBus.Properties", |
| 40 | signal_name="PropertiesChanged", |
| 41 | path="/org/openbmc/settings/host0") |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 42 | |
| 43 | # Create the dbus properties |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 44 | for i in settings[DBUS_NAME].iterkeys(): |
| 45 | shk = settings[DBUS_NAME][i] |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 46 | self.set_settings_property(shk['name'], |
| 47 | shk['type'], |
| 48 | shk['default']) |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 49 | # Done with consuming factory settings. |
| 50 | self.adminmode = False |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 51 | |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 52 | def get_bmc_value(self, name): |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 53 | try: |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 54 | with open(path.join(self.path, name), 'r') as f: |
| 55 | return f.read() |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 56 | except (IOError): |
| 57 | pass |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 58 | return None |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 59 | |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 60 | # Create dbus properties based on bmc value. |
| 61 | # This will be either a value previously set, |
| 62 | # or the default file value if the BMC value |
| 63 | # does not exist. |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 64 | def set_settings_property(self, name, type, value): |
| 65 | bmcv = self.get_bmc_value(name) |
| 66 | if bmcv: |
| 67 | value = bmcv |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 68 | if type == "i": |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 69 | self.Set(DBUS_NAME, name, int(value)) |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 70 | elif type == "s": |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 71 | self.Set(DBUS_NAME, name, str(value)) |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 72 | elif type == "b": |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 73 | self.Set(DBUS_NAME, name, bool(value)) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 74 | |
| 75 | # Save the settings to the BMC. This will write the settings value in |
| 76 | # individual files named by the property name to the BMC. |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 77 | def set_system_settings(self, name, value): |
| 78 | bmcv = self.get_bmc_value(name) |
| 79 | if bmcv != value: |
| 80 | filepath = path.join(self.path, name) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 81 | with open(filepath, 'w') as f: |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 82 | f.write(str(value)) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 83 | |
| 84 | # Signal handler for when one ore more settings properties were updated. |
| 85 | # This will sync the changes to the BMC. |
Brad Bishop | 2a9fe66 | 2016-08-31 12:37:35 -0400 | [diff] [blame] | 86 | def settings_signal_handler( |
| 87 | self, interface_name, changed_properties, invalidated_properties): |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 88 | for name, value in changed_properties.items(): |
| 89 | self.set_system_settings(name, value) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 90 | |
| 91 | # Placeholder signal. Needed to register the settings interface. |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 92 | @dbus.service.signal(DBUS_NAME, signature='s') |
| 93 | def SettingsUpdated(self, sname): |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 94 | pass |
| 95 | |
Vishwanatha Subbanna | 5b090c6 | 2016-09-21 15:49:26 +0530 | [diff] [blame] | 96 | def validate_regex(self, regex, value): |
| 97 | if not re.compile(regex).search(value): |
| 98 | raise ValueError, "Invalid input. Data does not satisfy regex" |
| 99 | |
| 100 | def validate_range(self, min, max, value): |
| 101 | if value not in range(min, max): |
| 102 | raise ValueError, "Invalid input. Data not in allowed range" |
| 103 | |
| 104 | def validate_list_ignore_case(self, lst, value): |
| 105 | if value.lower() not in map(lambda val: val.lower(), lst): |
| 106 | raise ValueError, "Invalid input. Data not in allowed values" |
| 107 | |
| 108 | # validate host network configuration |
| 109 | # need "ipaddress=,prefix=,gateway=,mac=,addr_type=" |
| 110 | # Must be able to handle any order |
| 111 | def validate_net_config(self, value): |
| 112 | if self.adminmode: |
| 113 | return |
| 114 | |
| 115 | # Need all of these to be given by the user. |
| 116 | user_config = [] |
| 117 | all_config = ['ipaddress', 'prefix', 'gateway', 'mac', 'addr_type'] |
| 118 | |
| 119 | # This has a hard data format mentioned above so no blanks allowed. |
| 120 | if value.count(" ") or value.count("=") != 5: |
| 121 | raise ValueError, "Invalid Network Data. No white spaces allowed" |
| 122 | |
| 123 | config = value.split(',') |
| 124 | for key_value in config: |
| 125 | key , value = key_value.split('=') |
| 126 | if not key or not value: |
| 127 | raise ValueError, "Invalid key or Data" |
| 128 | |
| 129 | # Add the current key seen so we can compare at the end to see |
| 130 | # if all values have been given |
| 131 | user_config.append(key.lower()) |
| 132 | |
| 133 | if key.lower() == 'ipaddress' or key.lower() == 'gateway': |
| 134 | IP(value) |
| 135 | |
| 136 | elif key.lower() == 'mac': |
| 137 | regex = '([a-fA-F0-9]{2}[:|\-]?){6}' |
| 138 | self.validate_regex(regex, value) |
| 139 | |
| 140 | elif key.lower() == 'prefix': |
| 141 | self.validate_range(0, 33, int(value)) |
| 142 | |
| 143 | elif key.lower() == 'addr_type': |
| 144 | allowed = ["STATIC", "DYNAMIC"] |
| 145 | self.validate_list_ignore_case(allowed, value) |
| 146 | |
| 147 | # Did user pass everything ?? |
| 148 | if set(all_config) - set(user_config): |
| 149 | raise ValueError, "Invalid Network Data. All information is mandatory" |
| 150 | |
| 151 | # Validate to see if the changes are in order |
| 152 | def input_validator(self, iface, proprty, value): |
| 153 | settings = s.SETTINGS |
| 154 | shk = {} |
| 155 | for key in settings[iface].iterkeys(): |
| 156 | if proprty == settings[iface][key]['name']: |
| 157 | shk = settings[iface][key] |
| 158 | break |
| 159 | |
| 160 | # User entered key is not present |
| 161 | if not shk: raise KeyError, "Invalid Property" |
| 162 | |
| 163 | if shk['validation'] == 'list': |
| 164 | self.validate_list_ignore_case(shk['allowed'], value) |
| 165 | |
| 166 | elif shk['validation'] == 'range': |
| 167 | self.validate_range(shk['min'], shk['max']+1, value) |
| 168 | |
| 169 | elif shk['validation'] == 'regex': |
| 170 | self.validate_regex(shk['regex'], value) |
| 171 | |
| 172 | elif shk['validation'] == 'custom': |
| 173 | getattr(self, shk['method'])(value) |
| 174 | |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 175 | if __name__ == '__main__': |
| 176 | dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) |
| 177 | |
Brad Bishop | 5ef7fe6 | 2016-05-17 08:39:17 -0400 | [diff] [blame] | 178 | bus = get_dbus() |
Adriana Kobylak | 41a925e | 2016-01-28 16:44:27 -0600 | [diff] [blame] | 179 | obj = HostSettingsObject(bus, OBJ_NAME, s.SETTINGS, "/var/lib/obmc/") |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 180 | mainloop = gobject.MainLoop() |
| 181 | |
Brad Bishop | 2cbef3d | 2016-08-31 12:40:13 -0400 | [diff] [blame] | 182 | obj.unmask_signals() |
| 183 | name = dbus.service.BusName(DBUS_NAME, bus) |
Adriana Kobylak | 4c60e5e | 2016-01-10 15:22:45 -0600 | [diff] [blame] | 184 | print "Running HostSettingsService" |
| 185 | mainloop.run() |