Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support python dict in sysctl_create #15

Merged
merged 4 commits into from May 2, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 11 additions & 7 deletions charmhelpers/core/sysctl.py
Expand Up @@ -31,18 +31,22 @@
def create(sysctl_dict, sysctl_file):
"""Creates a sysctl.conf file from a YAML associative array

:param sysctl_dict: a YAML-formatted string of sysctl options eg "{ 'kernel.max_pid': 1337 }"
:param sysctl_dict: a dict or YAML-formatted string of sysctl
options eg "{ 'kernel.max_pid': 1337 }"
:type sysctl_dict: str
:param sysctl_file: path to the sysctl file to be saved
:type sysctl_file: str or unicode
:returns: None
"""
try:
sysctl_dict_parsed = yaml.safe_load(sysctl_dict)
except yaml.YAMLError:
log("Error parsing YAML sysctl_dict: {}".format(sysctl_dict),
level=ERROR)
return
if type(sysctl_dict) is not dict:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would probably be more flexible if it used:

if isinstance(syctl_dict, dict):

as it would allow dict like objects to also be used. If you wanted to be really flexible, then you could use:

if isinstance(sysctl_dict, collections.abc.Mapping):

Then it would allow any object that supports the dictionary specific methods (e.g. getattr, iter, etc.) to be used.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might consider using if not isinstance(sysctl_dict, collections.Mapping): instead, on the off chance that the charm wants to pass in, e.g., an OrderedDict instead. Not really a blocker, though, since the order wouldn't be preserved with the existing code, either.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, collections.Mapping is Py2 compatible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

importing collections.Mapping raises a warning under Py3.7, and should be collections.abc.Mapping. We might need a conditional import.

try:
sysctl_dict_parsed = yaml.safe_load(sysctl_dict)
except yaml.YAMLError:
log("Error parsing YAML sysctl_dict: {}".format(sysctl_dict),
level=ERROR)
return
else:
sysctl_dict_parsed = sysctl_dict

with open(sysctl_file, "w") as fd:
for key, value in sysctl_dict_parsed.items():
Expand Down
19 changes: 19 additions & 0 deletions tests/core/test_sysctl.py
Expand Up @@ -53,6 +53,25 @@ def test_create(self, mock_open):
"sysctl", "-p",
"/etc/sysctl.d/test-sysctl.conf"])

@patch(builtin_open)
def test_create_with_dict(self, mock_open):
"""Test create sysctl method"""
_file = MagicMock(spec=io.FileIO)
mock_open.return_value = _file

create({"kernel.max_pid": 1337}, "/etc/sysctl.d/test-sysctl.conf")

_file.__enter__().write.assert_called_with("kernel.max_pid=1337\n")

self.log.assert_called_with(
"Updating sysctl_file: /etc/sysctl.d/test-sysctl.conf"
" values: {'kernel.max_pid': 1337}",
level='DEBUG')

self.check_call.assert_called_with([
"sysctl", "-p",
"/etc/sysctl.d/test-sysctl.conf"])

@patch(builtin_open)
def test_create_invalid_argument(self, mock_open):
"""Test create sysctl with an invalid argument"""
Expand Down