blob: 3e09ce51f2570cda119b57a44937a00c38bdf04c [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
Brad Bishop966fcd52016-09-29 09:22:32 -040013settings_file_path = os.path.join(
14 sys.prefix,
15 'share',
16 'phosphor-settings')
Adriana Kobylak256be782016-08-24 15:43:16 -050017sys.path.insert(1, settings_file_path)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060018import settings_file as s
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053019import re
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060020
21DBUS_NAME = 'org.openbmc.settings.Host'
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060022CONTROL_INTF = 'org.openbmc.Settings'
23
Sergey Solomin4a2433f2016-10-03 10:22:57 -050024def walk_nest(d, keys =()):
25 """Arrange dictionary keys and values.
26
27 Walk the dictionary and establish every possible path
28 returned to and processed by 'create_object' below
29 """
30 if isinstance(d, dict):
31 for k, v in d.iteritems():
32 for rv in walk_nest(v, keys + (k, )):
33 yield rv
34 else:
35 yield keys, d
36
37def create_object(settings):
38 """Create and format objects.
39
40 Parse dictionary file and return all objects and main properties
41 (name, type, default) for each attribute in the following format:
42 [obj_name, attr_ name, attr_ type, default]
43 """
44 allobjects = {}
45 for compound_key, val in walk_nest(settings):
46 obj_name = compound_key[0].lower()
47 obj_name = obj_name.replace(".","/")
48 obj_name = "/" + obj_name + "0"
49
50 for i in compound_key[1:len(compound_key)-2]:
51 obj_name = obj_name + "/" + i
52
53 setting = compound_key[len(compound_key) - 2]
54 attribute = compound_key[len(compound_key) - 1]
55
56 o = allobjects.setdefault(obj_name, {})
57 s = o.setdefault(setting, {'name': None, 'type': None, 'default': None})
58 s[attribute] = val
59
60 return allobjects
Brad Bishop2a9fe662016-08-31 12:37:35 -040061
Brad Bishop5ef7fe62016-05-17 08:39:17 -040062class HostSettingsObject(DbusProperties):
Adriana Kobylak41a925e2016-01-28 16:44:27 -060063 def __init__(self, bus, name, settings, path):
Brad Bishopc1e5e9f2016-09-29 09:40:01 -040064 super(HostSettingsObject, self).__init__(
65 conn=bus,
66 object_path=name,
67 validator=self.input_validator)
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053068 self.bus = bus
Adriana Kobylak41a925e2016-01-28 16:44:27 -060069 self.path = path
Sergey Solomin4a2433f2016-10-03 10:22:57 -050070 self.name = name
71 self.settings = settings
72
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053073 # Needed to ignore the validation on default networkconfig values as
74 # opposed to user giving the same.
75 self.adminmode = True
76
Adriana Kobylak41a925e2016-01-28 16:44:27 -060077 if not os.path.exists(path):
78 os.mkdir(path)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060079
80 # Listen to changes in the property values and sync them to the BMC
Brad Bishop2a9fe662016-08-31 12:37:35 -040081 bus.add_signal_receiver(
82 self.settings_signal_handler,
83 dbus_interface="org.freedesktop.DBus.Properties",
84 signal_name="PropertiesChanged",
Sergey Solomin4a2433f2016-10-03 10:22:57 -050085 path=name)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060086
87 # Create the dbus properties
Sergey Solomin4a2433f2016-10-03 10:22:57 -050088 for setting in settings.itervalues():
89 self.set_settings_property(setting['name'],
90 setting['type'],
91 setting['default'])
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +053092 # Done with consuming factory settings.
93 self.adminmode = False
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060094
Adriana Kobylak41a925e2016-01-28 16:44:27 -060095 def get_bmc_value(self, name):
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060096 try:
Adriana Kobylak41a925e2016-01-28 16:44:27 -060097 with open(path.join(self.path, name), 'r') as f:
98 return f.read()
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -060099 except (IOError):
100 pass
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600101 return None
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600102
Brad Bishop2a9fe662016-08-31 12:37:35 -0400103 # Create dbus properties based on bmc value.
104 # This will be either a value previously set,
105 # or the default file value if the BMC value
106 # does not exist.
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500107 def set_settings_property(self, attr_name, attr_type, value):
108 bmcv = self.get_bmc_value(attr_name)
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600109 if bmcv:
110 value = bmcv
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500111 if attr_type == "i":
112 self.Set(DBUS_NAME, attr_name, int(value))
113 elif attr_type == "s":
114 self.Set(DBUS_NAME, attr_name, str(value))
115 elif attr_type == "b":
116 self.Set(DBUS_NAME, attr_name, bool(value))
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600117
118 # Save the settings to the BMC. This will write the settings value in
119 # individual files named by the property name to the BMC.
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600120 def set_system_settings(self, name, value):
121 bmcv = self.get_bmc_value(name)
122 if bmcv != value:
123 filepath = path.join(self.path, name)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600124 with open(filepath, 'w') as f:
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600125 f.write(str(value))
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600126
127 # Signal handler for when one ore more settings properties were updated.
128 # This will sync the changes to the BMC.
Brad Bishop2a9fe662016-08-31 12:37:35 -0400129 def settings_signal_handler(
130 self, interface_name, changed_properties, invalidated_properties):
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600131 for name, value in changed_properties.items():
132 self.set_system_settings(name, value)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600133
134 # Placeholder signal. Needed to register the settings interface.
Adriana Kobylak41a925e2016-01-28 16:44:27 -0600135 @dbus.service.signal(DBUS_NAME, signature='s')
136 def SettingsUpdated(self, sname):
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600137 pass
138
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530139 def validate_regex(self, regex, value):
140 if not re.compile(regex).search(value):
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400141 raise ValueError("Invalid input. Data does not satisfy regex")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530142
143 def validate_range(self, min, max, value):
144 if value not in range(min, max):
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400145 raise ValueError("Invalid input. Data not in allowed range")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530146
147 def validate_list_ignore_case(self, lst, value):
148 if value.lower() not in map(lambda val: val.lower(), lst):
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400149 raise ValueError("Invalid input. Data not in allowed values")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530150
151 # validate host network configuration
152 # need "ipaddress=,prefix=,gateway=,mac=,addr_type="
153 # Must be able to handle any order
154 def validate_net_config(self, value):
155 if self.adminmode:
156 return
157
158 # Need all of these to be given by the user.
159 user_config = []
160 all_config = ['ipaddress', 'prefix', 'gateway', 'mac', 'addr_type']
161
162 # This has a hard data format mentioned above so no blanks allowed.
163 if value.count(" ") or value.count("=") != 5:
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400164 raise ValueError("Invalid Network Data. No white spaces allowed")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530165
166 config = value.split(',')
167 for key_value in config:
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400168 key, value = key_value.split('=')
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530169 if not key or not value:
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400170 raise ValueError("Invalid key or Data")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530171
172 # Add the current key seen so we can compare at the end to see
173 # if all values have been given
174 user_config.append(key.lower())
175
176 if key.lower() == 'ipaddress' or key.lower() == 'gateway':
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400177 IP(value)
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530178
179 elif key.lower() == 'mac':
180 regex = '([a-fA-F0-9]{2}[:|\-]?){6}'
181 self.validate_regex(regex, value)
182
183 elif key.lower() == 'prefix':
184 self.validate_range(0, 33, int(value))
185
186 elif key.lower() == 'addr_type':
187 allowed = ["STATIC", "DYNAMIC"]
188 self.validate_list_ignore_case(allowed, value)
189
190 # Did user pass everything ??
191 if set(all_config) - set(user_config):
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400192 raise ValueError(
193 "Invalid Network Data. All information is mandatory")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530194
195 # Validate to see if the changes are in order
196 def input_validator(self, iface, proprty, value):
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530197 # User entered key is not present
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500198 shk = None
199 for attr in self.settings.itervalues():
200 if attr['name'] == proprty:
201 shk = attr
202
203 if shk is None:
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400204 raise KeyError("Invalid Property")
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530205
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500206 validation = shk.get('validation', None)
207
208 if validation == 'list':
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530209 self.validate_list_ignore_case(shk['allowed'], value)
210
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500211 elif validation == 'range':
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530212 self.validate_range(shk['min'], shk['max']+1, value)
213
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500214 elif validation == 'regex':
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530215 self.validate_regex(shk['regex'], value)
216
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500217 elif validation == 'custom':
Brad Bishopc1e5e9f2016-09-29 09:40:01 -0400218 getattr(self, shk['method'])(value)
Vishwanatha Subbanna5b090c62016-09-21 15:49:26 +0530219
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600220if __name__ == '__main__':
221 dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
222
Brad Bishop5ef7fe62016-05-17 08:39:17 -0400223 bus = get_dbus()
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500224 allobjects = create_object(s.SETTINGS)
225 lastobject = None
226 objs = []
227 for o, settings in allobjects.iteritems():
228 objs.append(HostSettingsObject(bus, o, settings, "/var/lib/obmc/"))
229 objs[-1].unmask_signals()
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600230
Sergey Solomin4a2433f2016-10-03 10:22:57 -0500231 mainloop = gobject.MainLoop()
Brad Bishop2cbef3d2016-08-31 12:40:13 -0400232 name = dbus.service.BusName(DBUS_NAME, bus)
Adriana Kobylak4c60e5e2016-01-10 15:22:45 -0600233 print "Running HostSettingsService"
234 mainloop.run()
Brad Bishop31c42f02016-09-29 09:35:32 -0400235
236# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4