summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDeepak Kodihalli <dkodihal@in.ibm.com>2017-08-27 02:46:47 -0500
committerPatrick Williams <patrick@stwcx.xyz>2017-08-30 19:54:21 +0000
commitdb8386263b86d967e78a5af7607fa4a3f677be1d (patch)
tree805fba0e67f0b740ff4cceacbd2e01108eed288b
parent242bc77c441eb7863e6bb7f513fe92016d6b0732 (diff)
downloadphosphor-settingsd-db8386263b86d967e78a5af7607fa4a3f677be1d.tar.gz
phosphor-settingsd-db8386263b86d967e78a5af7607fa4a3f677be1d.zip
settings objects: support multiple interfaces
A settings object had the limitation that it could implement a single, corresponding settings interface. This was found to be too restrictive because there are use-cases (such as in the boot settings area) that would simplify describing settings with objects that can implement more than one interface. This commit adds the ability for settings objects to implement multiple interfaces. The settings config yaml now should contain a list of interface(s) under each settings object. Change-Id: I90dec9e766e1afd1b0c8616e491832714bbd069d Signed-off-by: Deepak Kodihalli <dkodihal@in.ibm.com>
-rw-r--r--settings_example.yaml66
-rw-r--r--settings_manager.mako.hpp174
2 files changed, 139 insertions, 101 deletions
diff --git a/settings_example.yaml b/settings_example.yaml
index 82b63be..e285953 100644
--- a/settings_example.yaml
+++ b/settings_example.yaml
@@ -1,31 +1,43 @@
-/xyz/openbmc_project/control/host0/boot_mode:
- Interface: xyz.openbmc_project.Control.Boot.Mode
- Properties:
- BootMode:
- Default: Mode::Modes::Safe
+# This settings object implements multiple interfaces
+/xyz/openbmc_project/control/host0/boot:
+ - Interface: xyz.openbmc_project.Control.Boot.Mode
+ Properties:
+ BootMode:
+ Default: Mode::Modes::Safe
+ - Interface: xyz.openbmc_project.Control.Boot.Source
+ Properties:
+ BootSource:
+ Default: Source::Sources::Default
-/xyz/openbmc_project/control/host1/boot_mode:
- Interface: xyz.openbmc_project.Control.Boot.Mode
- Properties:
- BootMode:
- Default: Mode::Modes::Regular
+# Same as the previous settings object, but for a different host
+/xyz/openbmc_project/control/host1/boot:
+ - Interface: xyz.openbmc_project.Control.Boot.Mode
+ Properties:
+ BootMode:
+ Default: Mode::Modes::Safe
+ - Interface: xyz.openbmc_project.Control.Boot.Source
+ Properties:
+ BootSource:
+ Default: Source::Sources::Default
+# This settings object has a property that needs to be validated, when changed.
/xyz/openbmc_project/network/host0/intf:
- Interface: xyz.openbmc_project.Network.MACAddress
- Properties:
- MACAddress:
- Default: '"00:00:00:00:00:00"'
- Validation:
- Type: "regex"
- Validator: '^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$'
+ - Interface: xyz.openbmc_project.Network.MACAddress
+ Properties:
+ MACAddress:
+ Default: '"00:00:00:00:00:00"'
+ Validation:
+ Type: "regex"
+ Validator: '^([0-9A-F]{2}[:-]){5}([0-9A-F]{2})$'
+
/xyz/openbmc_project/control/host0/power_cap:
- Interface: xyz.openbmc_project.Control.Power.Cap
- Properties:
- PowerCap:
- Default: 0
- Validation:
- Type: "range"
- Validator: "0..1000"
- Unit: "Watts"
- PowerCapEnable:
- Default: 'false'
+ - Interface: xyz.openbmc_project.Control.Power.Cap
+ Properties:
+ PowerCap:
+ Default: 0
+ Validation:
+ Type: "range"
+ Validator: "0..1000"
+ Unit: "Watts"
+ PowerCapEnable:
+ Default: 'false'
diff --git a/settings_manager.mako.hpp b/settings_manager.mako.hpp
index b1c3988..1dbe550 100644
--- a/settings_manager.mako.hpp
+++ b/settings_manager.mako.hpp
@@ -7,7 +7,6 @@ from collections import defaultdict
objects = list(settingsDict.viewkeys())
sdbusplus_namespaces = []
sdbusplus_includes = []
-interfaces = []
props = defaultdict(list)
validators = defaultdict(tuple)
@@ -17,19 +16,22 @@ def get_setting_sdbusplus_type(setting_intf):
setting = setting[:i] + '::server::' + setting[i+2:]
return setting
-def get_setting_type(setting_intf):
- setting = setting_intf.replace('.', '::')
- return setting
+def get_setting_type(path):
+ path = path[1:]
+ path = path.replace('/', '::')
+ return path
%>\
#pragma once
% for object in objects:
+ % for item in settingsDict[object]:
<%
- include = settingsDict[object]['Interface']
+ include = item['Interface']
include = include.replace('.', '/')
include = include + "/server.hpp"
sdbusplus_includes.append(include)
%>\
+ % endfor
% endfor
#include <cereal/archives/json.hpp>
#include <fstream>
@@ -47,12 +49,14 @@ def get_setting_type(setting_intf):
% endfor
% for object in objects:
+ % for item in settingsDict[object]:
<%
- ns = get_setting_sdbusplus_type(settingsDict[object]['Interface'])
+ ns = get_setting_sdbusplus_type(item['Interface'])
i = ns.rfind('::')
ns = ns[:i]
sdbusplus_namespaces.append(ns)
%>\
+ % endfor
% endfor
namespace phosphor
@@ -83,31 +87,33 @@ using namespace ${n};
% for object in objects:
<%
- intf = settingsDict[object]['Interface']
- interfaces.append(intf)
- if intf not in props:
- for property, property_metadata in settingsDict[object]['Properties'].items():
- props[intf].append(property)
- for attribute, value in property_metadata.items():
- if attribute == 'Validation':
- if value['Type'] == "range":
- validators[property] = (value['Type'], value['Validator'], value['Unit'])
- else:
- validators[property] = (value['Type'], value['Validator'])
-%>\
-% endfor
-% for intf in set(interfaces):
-<%
- ns = intf.split(".")
- sdbusplus_type = get_setting_sdbusplus_type(intf)
+ ns = object.split('/')
+ ns.pop(0)
%>\
% for n in ns:
namespace ${n}
{
% endfor
-
-using Base = ${sdbusplus_type};
-<% parent = "sdbusplus::server::object::object" + "<" + sdbusplus_type + ">" %>\
+<%
+ interfaces = []
+ aliases = []
+ for item in settingsDict[object]:
+ interfaces.append(item['Interface'])
+ for name, meta in item['Properties'].items():
+ if 'Validation' in meta:
+ dict = meta['Validation']
+ if dict['Type'] == "range":
+ validators[name] = (dict['Type'], dict['Validator'], dict['Unit'])
+ else:
+ validators[name] = (dict['Type'], dict['Validator'])
+%>
+% for index, intf in enumerate(interfaces):
+using Iface${index} = ${get_setting_sdbusplus_type(intf)};
+<% aliases.append("Iface" + str(index)) %>\
+% endfor
+<%
+ parent = "sdbusplus::server::object::object" + "<" + ", ".join(aliases) + ">"
+%>\
using Parent = ${parent};
class Impl : public Parent
@@ -120,15 +126,16 @@ class Impl : public Parent
}
virtual ~Impl() = default;
-% for arg in props[intf]:
-<% t = arg[:1].lower() + arg[1:] %>\
-<% fname = "validate"+arg %>\
- decltype(std::declval<Base>().${t}()) ${t}(decltype(std::declval<Base>().${t}()) value) override
+% for index, item in enumerate(settingsDict[object]):
+ % for propName, metaDict in item['Properties'].items():
+<% t = propName[:1].lower() + propName[1:] %>\
+<% fname = "validate" + propName %>\
+ decltype(std::declval<Iface${index}>().${t}()) ${t}(decltype(std::declval<Iface${index}>().${t}()) value) override
{
- auto result = Base::${t}();
+ auto result = Iface${index}::${t}();
if (value != result)
{
- % if arg in validators.keys():
+ % if propName in validators:
if (!${fname}(value))
{
namespace error =
@@ -137,7 +144,7 @@ class Impl : public Parent
phosphor::logging::xyz::openbmc_project::Common;
phosphor::logging::report<error::InvalidArgument>(
metadata::InvalidArgument::ARGUMENT_NAME("${t}"),
- % if validators[arg][0] != "regex":
+ % if validators[propName][0] != "regex":
metadata::InvalidArgument::ARGUMENT_VALUE(std::to_string(value).c_str()));
% else:
metadata::InvalidArgument::ARGUMENT_VALUE(value.c_str()));
@@ -151,55 +158,58 @@ class Impl : public Parent
fs::create_directories(p.parent_path());
std::ofstream os(p.c_str(), std::ios::binary);
cereal::JSONOutputArchive oarchive(os);
- result = Base::${t}(value);
+ result = Iface${index}::${t}(value);
oarchive(*this);
}
return result;
}
- using Base::${t};
+ using Iface${index}::${t};
+ % endfor
% endfor
private:
fs::path path;
-% for arg in props[intf]:
-% if arg in validators.keys():
-<% funcName = "validate"+arg %>\
-<% t = arg[:1].lower() + arg[1:] %>\
+% for index, item in enumerate(settingsDict[object]):
+ % for propName, metaDict in item['Properties'].items():
+<% t = propName[:1].lower() + propName[1:] %>\
+<% fname = "validate" + propName %>\
+ % if propName in validators:
- bool ${funcName}(decltype(std::declval<Base>().${t}()) value)
+ bool ${fname}(decltype(std::declval<Iface${index}>().${t}()) value)
{
bool matched = false;
- % if (arg in validators.keys()) and (validators[arg][0] == 'regex'):
- std::regex regexToCheck("${validators[arg][1]}");
+ % if (validators[propName][0] == 'regex'):
+ std::regex regexToCheck("${validators[propName][1]}");
matched = std::regex_search(value, regexToCheck);
if (!matched)
{
- std::string err = "Input parameter for ${arg} is invalid "
+ std::string err = "Input parameter for ${propName} is invalid "
"Input: " + value + " not in the format of this regex: "
- "${validators[arg][1]}";
+ "${validators[propName][1]}";
using namespace phosphor::logging;
log<level::ERR>(err.c_str());
}
- % elif (arg in validators.keys()) and (validators[arg][0] == 'range'):
-<% lowhigh = re.split('\.\.', validators[arg][1]) %>\
+ % elif (validators[propName][0] == 'range'):
+<% lowhigh = re.split('\.\.', validators[propName][1]) %>\
if ((value <= ${lowhigh[1]}) && (value >= ${lowhigh[0]}))
{
matched = true;
}
else
{
- std::string err = "Input parameter for ${arg} is invalid "
+ std::string err = "Input parameter for ${propName} is invalid "
"Input: " + std::to_string(value) + "in uint: "
- "${validators[arg][2]} is not in range:${validators[arg][1]}";
+ "${validators[propName][2]} is not in range:${validators[propName][1]}";
using namespace phosphor::logging;
log<level::ERR>(err.c_str());
}
- % elif (arg in validators.keys()):
- <% assert("Unknown validation type: arg") %>\
+ % else:
+ <% assert("Unknown validation type: propName") %>\
% endif
return matched;
}
-% endif
+ % endif
+ % endfor
% endfor
};
@@ -208,33 +218,47 @@ void save(Archive& a,
const Impl& setting)
{
<%
- args = ["setting." + p[:1].lower() + p[1:] + "()" for p in props[intf]]
- args = ','.join(args)
+props = []
+for index, item in enumerate(settingsDict[object]):
+ intfProps = ["setting." + propName[:1].lower() + propName[1:] + "()" for \
+ propName, metaDict in item['Properties'].items()]
+ props.extend(intfProps)
+props = ', '.join(props)
%>\
- a(${args});
+ a(${props});
}
template<class Archive>
void load(Archive& a,
Impl& setting)
{
-% for arg in props[intf]:
-<% t = "setting." + arg[:1].lower() + arg[1:] + "()" %>\
- decltype(${t}) ${arg}{};
+<% props = [] %>\
+% for index, item in enumerate(settingsDict[object]):
+ % for prop, metaDict in item['Properties'].items():
+<%
+ t = "setting." + prop[:1].lower() + prop[1:] + "()"
+ props.append(prop)
+%>\
+ decltype(${t}) ${prop}{};
+ % endfor
% endfor
+<% props = ', '.join(props) %>
+ a(${props});
+<% props = [] %>
+% for index, item in enumerate(settingsDict[object]):
+ % for prop, metaDict in item['Properties'].items():
<%
- args = ','.join(props[intf])
+ t = "setting." + prop[:1].lower() + prop[1:] + "(" + prop + ")"
%>\
- a(${args});
-% for arg in props[intf]:
-<% t = "setting." + arg[:1].lower() + arg[1:] + "(" + arg + ")" %>\
${t};
+ % endfor
% endfor
}
% for n in reversed(ns):
} // namespace ${n}
% endfor
+
% endfor
/** @class Manager
@@ -259,22 +283,19 @@ class Manager
fs::path path{};
settings =
std::make_tuple(
-% for index, object in enumerate(objects):
-<% type = get_setting_type(settingsDict[object]['Interface']) + "::Impl" %>\
+% for index, path in enumerate(objects):
+<% type = get_setting_type(path) + "::Impl" %>\
std::make_unique<${type}>(
bus,
% if index < len(settingsDict) - 1:
- "${object}"),
+ "${path}"),
% else:
- "${object}"));
+ "${path}"));
% endif
% endfor
-% for index, object in enumerate(objects):
- % for property, value in settingsDict[object]['Properties'].items():
-<% p = property[:1].lower() + property[1:] %>\
-<% defaultValue = value['Default'] %>\
- path = fs::path(SETTINGS_PERSIST_PATH) / "${object}";
+% for index, path in enumerate(objects):
+ path = fs::path(SETTINGS_PERSIST_PATH) / "${path}";
path += persistent::fileSuffix;
if (fs::exists(path))
{
@@ -284,10 +305,15 @@ class Manager
}
else
{
+ % for item in settingsDict[path]:
+ % for propName, metaDict in item['Properties'].items():
+<% p = propName[:1].lower() + propName[1:] %>\
+<% defaultValue = metaDict['Default'] %>\
std::get<${index}>(settings)->
- ${get_setting_sdbusplus_type(settingsDict[object]['Interface'])}::${p}(${defaultValue});
- }
+ ${get_setting_sdbusplus_type(item['Interface'])}::${p}(${defaultValue});
+ % endfor
% endfor
+ }
std::get<${index}>(settings)->emit_object_added();
% endfor
@@ -296,8 +322,8 @@ class Manager
private:
/* @brief Composition of settings objects. */
std::tuple<
-% for index, object in enumerate(objects):
-<% type = get_setting_type(settingsDict[object]['Interface']) + "::Impl" %>\
+% for index, path in enumerate(objects):
+<% type = get_setting_type(path) + "::Impl" %>\
% if index < len(settingsDict) - 1:
std::unique_ptr<${type}>,
% else:
OpenPOWER on IntegriCloud