blob: 997c56fc6d0ac8919434aa41f5bb5ce9073f465e [file] [log] [blame]
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -06001#!/usr/bin/python -u
2
3import gobject
4import dbus
5import dbus.service
6import dbus.mainloop.glib
Adriana Kobylak41a925e2016-01-28 16:44:27 -06007import os
8import os.path as path
Adriana Kobylak256be782016-08-24 15:43:16 -05009import sys
Brad Bishop5ef7fe62016-05-17 08:39:17 -040010from obmc.dbuslib.bindings import DbusProperties, get_dbus
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053011from IPy import IP
Adriana Kobylak256be782016-08-24 15:43:16 -050012
13settings_file_path = os.path.join(sys.prefix, 'share/obmc-phosphor-settings')
14sys.path.insert(1, settings_file_path)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060015import settings_file as s
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053016import re
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060017
18DBUS_NAME = 'org.openbmc.settings.Host'
19OBJ_NAME = '/org/openbmc/settings/host0'
20CONTROL_INTF = 'org.openbmc.Settings'
21
Brad Bishop2a9fe662016-08-31 12:37:35 -040022
Brad Bishop5ef7fe62016-05-17 08:39:17 -040023class HostSettingsObject(DbusProperties):
Adriana Kobylak41a925e2016-01-28 16:44:27 -060024 def __init__(self, bus, name, settings, path):
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053025 super(HostSettingsObject, self).__init__(conn=bus, object_path=name,
26 validator=self.input_validator)
27 self.bus = bus
Adriana Kobylak41a925e2016-01-28 16:44:27 -060028 self.path = path
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053029 # Needed to ignore the validation on default networkconfig values as
30 # opposed to user giving the same.
31 self.adminmode = True
32
Adriana Kobylak41a925e2016-01-28 16:44:27 -060033 if not os.path.exists(path):
34 os.mkdir(path)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060035
36 # Listen to changes in the property values and sync them to the BMC
Brad Bishop2a9fe662016-08-31 12:37:35 -040037 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 Kobylak4c60e5e2016-01-10 15:22:45 -060042
43 # Create the dbus properties
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053044 for i in settings[DBUS_NAME].iterkeys():
45 shk = settings[DBUS_NAME][i]
Adriana Kobylak41a925e2016-01-28 16:44:27 -060046 self.set_settings_property(shk['name'],
47 shk['type'],
48 shk['default'])
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053049 # Done with consuming factory settings.
50 self.adminmode = False
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060051
Adriana Kobylak41a925e2016-01-28 16:44:27 -060052 def get_bmc_value(self, name):
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060053 try:
Adriana Kobylak41a925e2016-01-28 16:44:27 -060054 with open(path.join(self.path, name), 'r') as f:
55 return f.read()
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060056 except (IOError):
57 pass
Adriana Kobylak41a925e2016-01-28 16:44:27 -060058 return None
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060059
Brad Bishop2a9fe662016-08-31 12:37:35 -040060 # 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 Kobylak41a925e2016-01-28 16:44:27 -060064 def set_settings_property(self, name, type, value):
65 bmcv = self.get_bmc_value(name)
66 if bmcv:
67 value = bmcv
Brad Bishop2a9fe662016-08-31 12:37:35 -040068 if type == "i":
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053069 self.Set(DBUS_NAME, name, int(value))
Brad Bishop2a9fe662016-08-31 12:37:35 -040070 elif type == "s":
Adriana Kobylak41a925e2016-01-28 16:44:27 -060071 self.Set(DBUS_NAME, name, str(value))
Brad Bishop2a9fe662016-08-31 12:37:35 -040072 elif type == "b":
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053073 self.Set(DBUS_NAME, name, bool(value))
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060074
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 Kobylak41a925e2016-01-28 16:44:27 -060077 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 Kobylak4c60e5e2016-01-10 15:22:45 -060081 with open(filepath, 'w') as f:
Adriana Kobylak41a925e2016-01-28 16:44:27 -060082 f.write(str(value))
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060083
84 # Signal handler for when one ore more settings properties were updated.
85 # This will sync the changes to the BMC.
Brad Bishop2a9fe662016-08-31 12:37:35 -040086 def settings_signal_handler(
87 self, interface_name, changed_properties, invalidated_properties):
Adriana Kobylak41a925e2016-01-28 16:44:27 -060088 for name, value in changed_properties.items():
89 self.set_system_settings(name, value)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060090
91 # Placeholder signal. Needed to register the settings interface.
Adriana Kobylak41a925e2016-01-28 16:44:27 -060092 @dbus.service.signal(DBUS_NAME, signature='s')
93 def SettingsUpdated(self, sname):
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060094 pass
95
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053096 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 Kobylak4c60e5e2016-01-10 15:22:45 -0600175if __name__ == '__main__':
176 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
177
Brad Bishop5ef7fe62016-05-17 08:39:17 -0400178 bus = get_dbus()
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600179 obj = HostSettingsObject(bus, OBJ_NAME, s.SETTINGS, "/var/lib/obmc/")
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600180 mainloop = gobject.MainLoop()
181
Brad Bishop2cbef3d2016-08-31 12:40:13 -0400182 obj.unmask_signals()
183 name = dbus.service.BusName(DBUS_NAME, bus)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600184 print "Running HostSettingsService"
185 mainloop.run()