{"action":"created","comment":{"url":"https://api.github.com/repos/saltstack/salt/pulls/comments/42516976","id":42516976,"diff_hunk":"@@ -0,0 +1,212 @@\n+# -*- coding: utf-8 -*-\n+'''\n+Module for interfacing with SysFS\n+\n+.. seealso:: https://www.kernel.org/doc/Documentation/filesystems/sysfs.txt\n+.. versionadded:: Boron\n+'''\n+# Import python libs\n+from __future__ import absolute_import\n+import logging\n+import os\n+import stat\n+\n+# Import external libs\n+import salt.ext.six as six\n+\n+# Import salt libs\n+import salt.utils\n+\n+log = logging.getLogger(__name__)\n+\n+\n+def __virtual__():\n+    '''\n+    Only work on Linux\n+    '''\n+    return salt.utils.is_linux()\n+\n+\n+def attr(key, value=None):\n+    '''\n+    Access/write a SysFS attribute.\n+    If the attribute is a symlink, it's destination is returned\n+\n+    :return: value or bool\n+\n+    CLI example:\n+     .. code-block:: bash\n+\n+        salt '*' sysfs.attr block/sda/queue/logical_block_size\n+    '''\n+    key = target(key)\n+\n+    if key is False:\n+        return False\n+    elif os.path.isdir(key):\n+        return key\n+    elif value is not None:\n+        return write(key, value)\n+    else:\n+        return read(key)\n+\n+\n+def write(key, value):\n+    '''\n+    Write a SysFS attribute/action\n+\n+    CLI example:\n+     .. code-block:: bash\n+\n+        salt '*' sysfs.write devices/system/cpu/cpu0/cpufreq/scaling_governor 'performance'\n+    '''\n+    try:\n+        key = target(key)\n+        log.trace('Writing {0} to {1}'.format(value, key))\n+        with open(key, 'w') as twriter:\n+            twriter.write('{0}\\n'.format(value))\n+            return True\n+    except:  # pylint: disable=bare-except\n+        return False\n+\n+\n+def read(key, root=None):\n+    '''\n+    Read from SysFS\n+    :param key: file or path in SysFS; if key is a list then root will be prefixed on each key\n+    :return: the full (tree of) SysFS attributes under key\n+\n+    CLI example:\n+     .. code-block:: bash\n+\n+        salt '*' sysfs.read class/net/em1/statistics\n+    '''\n+\n+    if not isinstance(key, six.string_types):\n+        res = {}\n+        for akey in key:\n+            ares = read(os.path.join(root, akey))\n+            if ares is not False:\n+                res[akey] = ares\n+        return res\n+\n+    key = target(os.path.join(root, key))\n+    if key is False:\n+        return False\n+    elif os.path.isdir(key):\n+        keys = interfaces(key)\n+        result = {}\n+        for subkey in keys['r'] + keys['rw']:\n+            subval = read(os.path.join(key, subkey))\n+            if subval is not False:\n+                subkeys = subkey.split('/')\n+                subkey = subkeys.pop()\n+                subresult = result\n+                if len(subkeys):\n+                    for skey in subkeys:\n+                        if skey not in subresult:\n+                            subresult[skey] = {}\n+                        subresult = subresult[skey]\n+                subresult[subkey] = subval\n+        return result\n+    else:\n+        try:\n+            log.trace('Reading {0}...'.format(key))\n+\n+            # Certain things in SysFS are pipes 'n such.\n+            # This opens it non-blocking, which prevents indefinite blocking\n+            with os.fdopen(os.open(key, os.O_RDONLY | os.O_NONBLOCK)) as treader:","path":"salt/modules/sysfs.py","position":118,"original_position":118,"commit_id":"210101c16c2527b86cfd83a18cd888267bc047b9","original_commit_id":"20fe70f00265a5e1da55c8f838c976878b995767","user":{"login":"The-Loeki","id":426731,"avatar_url":"https://avatars.githubusercontent.com/u/426731?v=3","gravatar_id":"","url":"https://api.github.com/users/The-Loeki","html_url":"https://github.com/The-Loeki","followers_url":"https://api.github.com/users/The-Loeki/followers","following_url":"https://api.github.com/users/The-Loeki/following{/other_user}","gists_url":"https://api.github.com/users/The-Loeki/gists{/gist_id}","starred_url":"https://api.github.com/users/The-Loeki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/The-Loeki/subscriptions","organizations_url":"https://api.github.com/users/The-Loeki/orgs","repos_url":"https://api.github.com/users/The-Loeki/repos","events_url":"https://api.github.com/users/The-Loeki/events{/privacy}","received_events_url":"https://api.github.com/users/The-Loeki/received_events","type":"User","site_admin":false},"body":"Well I'd like to do that, but I don't think it's possible?\r\n\r\nTake a good look at the admittedly somewhat exotic construct here; \r\nI'm using `os.open()` and passing that on to `os.fdopen()` rather than `open()` here for the very very specific `os.O_NONBLOCK` option. \r\n\r\nIIRC I did that because that was the only way to get that NONBLOCK option in the opener; `open()`, and therefore `salt.utils.fopen()` only supports 'mode', but I need that flag.\r\n\r\nAnd we *need* that non-block option because there's some very exotic stuff in SysFS (pipes & sockets & such) \r\n\r\nPut differently; aside from the obvious DRY args I see that the main reason this util exists is to set the CLOEXEC option.\r\n\r\nI haven't tested this yet, but I think there's a os.O_CLOEXEC which I could just add to get to the same point.\r\nSecondly, if that turns out to be true, wouldn't that be a nice fix for salt.utils.fopen()? The fcntl wrap and race condition mentioned in the comments would both be replaced for a construct very similar to the one here.\r\n\r\nI'm quite willing to patch both if that is the preferred course of action ;) \r\n\r\n@cachedout, what do you think? ","created_at":"2015-10-20T16:12:56Z","updated_at":"2015-10-20T16:12:56Z","html_url":"https://github.com/saltstack/salt/pull/28089#discussion_r42516976","pull_request_url":"https://api.github.com/repos/saltstack/salt/pulls/28089","_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments/42516976"},"html":{"href":"https://github.com/saltstack/salt/pull/28089#discussion_r42516976"},"pull_request":{"href":"https://api.github.com/repos/saltstack/salt/pulls/28089"}}},"pull_request":{"url":"https://api.github.com/repos/saltstack/salt/pulls/28089","id":48076582,"html_url":"https://github.com/saltstack/salt/pull/28089","diff_url":"https://github.com/saltstack/salt/pull/28089.diff","patch_url":"https://github.com/saltstack/salt/pull/28089.patch","issue_url":"https://api.github.com/repos/saltstack/salt/issues/28089","number":28089,"state":"open","locked":false,"title":"Contribution: SysFS module","user":{"login":"The-Loeki","id":426731,"avatar_url":"https://avatars.githubusercontent.com/u/426731?v=3","gravatar_id":"","url":"https://api.github.com/users/The-Loeki","html_url":"https://github.com/The-Loeki","followers_url":"https://api.github.com/users/The-Loeki/followers","following_url":"https://api.github.com/users/The-Loeki/following{/other_user}","gists_url":"https://api.github.com/users/The-Loeki/gists{/gist_id}","starred_url":"https://api.github.com/users/The-Loeki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/The-Loeki/subscriptions","organizations_url":"https://api.github.com/users/The-Loeki/orgs","repos_url":"https://api.github.com/users/The-Loeki/repos","events_url":"https://api.github.com/users/The-Loeki/events{/privacy}","received_events_url":"https://api.github.com/users/The-Loeki/received_events","type":"User","site_admin":false},"body":"I'd like this module to be considered for inclusion.\r\n\r\nIt's a generic wrap around SysFS interfaces. It's able to read out (batches of) SysFS values, and distill interfaces from them for easy utilization in other modules.\r\n\r\nIn particular the `interfaces` function is *very* useful in this regard;\r\n\r\n```\r\nsalt-call sysfs.interfaces block/bcache0/bcache\r\nlocal:\r\n    ----------\r\n    r:\r\n        - state\r\n        - partial_stripes_expensive\r\n        - writeback_rate_debug\r\n        - stripe_size\r\n        - dirty_data\r\n        - stats_five_minute/cache_hits\r\n        - stats_five_minute/cache_bypass_misses\r\n        - stats_five_minute/bypassed\r\n        - stats_five_minute/cache_readaheads\r\n        - stats_five_minute/cache_hit_ratio\r\n        - stats_five_minute/cache_miss_collisions\r\n        - stats_five_minute/cache_misses\r\n        - stats_five_minute/cache_bypass_hits\r\n        - stats_total/cache_hits\r\n        - stats_total/cache_bypass_misses\r\n        - stats_total/bypassed\r\n        - stats_total/cache_readaheads\r\n        - stats_total/cache_hit_ratio\r\n        - stats_total/cache_miss_collisions\r\n        - stats_total/cache_misses\r\n        - stats_total/cache_bypass_hits\r\n        - stats_day/cache_hits\r\n        - stats_day/cache_bypass_misses\r\n        - stats_day/bypassed\r\n        - stats_day/cache_readaheads\r\n        - stats_day/cache_hit_ratio\r\n        - stats_day/cache_miss_collisions\r\n        - stats_day/cache_misses\r\n        - stats_day/cache_bypass_hits\r\n        - stats_hour/cache_hits\r\n        - stats_hour/cache_bypass_misses\r\n        - stats_hour/bypassed\r\n        - stats_hour/cache_readaheads\r\n        - stats_hour/cache_hit_ratio\r\n        - stats_hour/cache_miss_collisions\r\n        - stats_hour/cache_misses\r\n        - stats_hour/cache_bypass_hits\r\n    rw:\r\n        - writeback_rate\r\n        - writeback_rate_update_seconds\r\n        - cache_mode\r\n        - writeback_delay\r\n        - label\r\n        - writeback_running\r\n        - writeback_metadata\r\n        - running\r\n        - writeback_rate_p_term_inverse\r\n        - sequential_cutoff\r\n        - writeback_percent\r\n        - writeback_rate_d_term\r\n        - readahead\r\n    w:\r\n        - stop\r\n        - clear_stats\r\n        - attach\r\n        - detach\r\n```\r\n\r\nNow you immediately know what you can do with this dev and start doing it w00t :):\r\n\r\n```\r\nintf = __salt__['sysfs.interfaces']('block/bcache0/bcache')\r\n\r\nstats = [key for key in intf['r] if key.startswith('stats')]\r\n\r\nstats = __salt__['sysfs.read']('block/bcache0/bcache', stats)\r\n```\r\nThe module's been running fine on 2014.7 & 2015.8 on a variety of Ubuntu's & CentOS's for quite some time now.","created_at":"2015-10-19T14:12:15Z","updated_at":"2015-10-20T16:12:56Z","closed_at":null,"merged_at":null,"merge_commit_sha":"21242cf671660cf324f4f9865d1f6a3ac34e3096","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/saltstack/salt/pulls/28089/commits","review_comments_url":"https://api.github.com/repos/saltstack/salt/pulls/28089/comments","review_comment_url":"https://api.github.com/repos/saltstack/salt/pulls/comments{/number}","comments_url":"https://api.github.com/repos/saltstack/salt/issues/28089/comments","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/210101c16c2527b86cfd83a18cd888267bc047b9","head":{"label":"The-Loeki:sysfs-module","ref":"sysfs-module","sha":"210101c16c2527b86cfd83a18cd888267bc047b9","user":{"login":"The-Loeki","id":426731,"avatar_url":"https://avatars.githubusercontent.com/u/426731?v=3","gravatar_id":"","url":"https://api.github.com/users/The-Loeki","html_url":"https://github.com/The-Loeki","followers_url":"https://api.github.com/users/The-Loeki/followers","following_url":"https://api.github.com/users/The-Loeki/following{/other_user}","gists_url":"https://api.github.com/users/The-Loeki/gists{/gist_id}","starred_url":"https://api.github.com/users/The-Loeki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/The-Loeki/subscriptions","organizations_url":"https://api.github.com/users/The-Loeki/orgs","repos_url":"https://api.github.com/users/The-Loeki/repos","events_url":"https://api.github.com/users/The-Loeki/events{/privacy}","received_events_url":"https://api.github.com/users/The-Loeki/received_events","type":"User","site_admin":false},"repo":{"id":44272780,"name":"salt","full_name":"The-Loeki/salt","owner":{"login":"The-Loeki","id":426731,"avatar_url":"https://avatars.githubusercontent.com/u/426731?v=3","gravatar_id":"","url":"https://api.github.com/users/The-Loeki","html_url":"https://github.com/The-Loeki","followers_url":"https://api.github.com/users/The-Loeki/followers","following_url":"https://api.github.com/users/The-Loeki/following{/other_user}","gists_url":"https://api.github.com/users/The-Loeki/gists{/gist_id}","starred_url":"https://api.github.com/users/The-Loeki/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/The-Loeki/subscriptions","organizations_url":"https://api.github.com/users/The-Loeki/orgs","repos_url":"https://api.github.com/users/The-Loeki/repos","events_url":"https://api.github.com/users/The-Loeki/events{/privacy}","received_events_url":"https://api.github.com/users/The-Loeki/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/The-Loeki/salt","description":"Infrastructure automation and management system","fork":true,"url":"https://api.github.com/repos/The-Loeki/salt","forks_url":"https://api.github.com/repos/The-Loeki/salt/forks","keys_url":"https://api.github.com/repos/The-Loeki/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/The-Loeki/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/The-Loeki/salt/teams","hooks_url":"https://api.github.com/repos/The-Loeki/salt/hooks","issue_events_url":"https://api.github.com/repos/The-Loeki/salt/issues/events{/number}","events_url":"https://api.github.com/repos/The-Loeki/salt/events","assignees_url":"https://api.github.com/repos/The-Loeki/salt/assignees{/user}","branches_url":"https://api.github.com/repos/The-Loeki/salt/branches{/branch}","tags_url":"https://api.github.com/repos/The-Loeki/salt/tags","blobs_url":"https://api.github.com/repos/The-Loeki/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/The-Loeki/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/The-Loeki/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/The-Loeki/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/The-Loeki/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/The-Loeki/salt/languages","stargazers_url":"https://api.github.com/repos/The-Loeki/salt/stargazers","contributors_url":"https://api.github.com/repos/The-Loeki/salt/contributors","subscribers_url":"https://api.github.com/repos/The-Loeki/salt/subscribers","subscription_url":"https://api.github.com/repos/The-Loeki/salt/subscription","commits_url":"https://api.github.com/repos/The-Loeki/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/The-Loeki/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/The-Loeki/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/The-Loeki/salt/issues/comments{/number}","contents_url":"https://api.github.com/repos/The-Loeki/salt/contents/{+path}","compare_url":"https://api.github.com/repos/The-Loeki/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/The-Loeki/salt/merges","archive_url":"https://api.github.com/repos/The-Loeki/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/The-Loeki/salt/downloads","issues_url":"https://api.github.com/repos/The-Loeki/salt/issues{/number}","pulls_url":"https://api.github.com/repos/The-Loeki/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/The-Loeki/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/The-Loeki/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/The-Loeki/salt/labels{/name}","releases_url":"https://api.github.com/repos/The-Loeki/salt/releases{/id}","created_at":"2015-10-14T19:57:28Z","updated_at":"2015-10-14T19:57:58Z","pushed_at":"2015-10-20T16:04:31Z","git_url":"git://github.com/The-Loeki/salt.git","ssh_url":"git@github.com:The-Loeki/salt.git","clone_url":"https://github.com/The-Loeki/salt.git","svn_url":"https://github.com/The-Loeki/salt","homepage":"saltstack.com","size":125138,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"develop"}},"base":{"label":"saltstack:develop","ref":"develop","sha":"b8271c7512da7e048019ee26422be9e7d6b795ab","user":{"login":"saltstack","id":1147473,"avatar_url":"https://avatars.githubusercontent.com/u/1147473?v=3","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"repo":{"id":1390248,"name":"salt","full_name":"saltstack/salt","owner":{"login":"saltstack","id":1147473,"avatar_url":"https://avatars.githubusercontent.com/u/1147473?v=3","gravatar_id":"","url":"https://api.github.com/users/saltstack","html_url":"https://github.com/saltstack","followers_url":"https://api.github.com/users/saltstack/followers","following_url":"https://api.github.com/users/saltstack/following{/other_user}","gists_url":"https://api.github.com/users/saltstack/gists{/gist_id}","starred_url":"https://api.github.com/users/saltstack/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/saltstack/subscriptions","organizations_url":"https://api.github.com/users/saltstack/orgs","repos_url":"https://api.github.com/users/saltstack/repos","events_url":"https://api.github.com/users/saltstack/events{/privacy}","received_events_url":"https://api.github.com/users/saltstack/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/saltstack/salt","description":"Infrastructure automation and management system","fork":false,"url":"https://api.github.com/repos/saltstack/salt","forks_url":"https://api.github.com/repos/saltstack/salt/forks","keys_url":"https://api.github.com/repos/saltstack/salt/keys{/key_id}","collaborators_url":"https://api.github.com/repos/saltstack/salt/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/saltstack/salt/teams","hooks_url":"https://api.github.com/repos/saltstack/salt/hooks","issue_events_url":"https://api.github.com/repos/saltstack/salt/issues/events{/number}","events_url":"https://api.github.com/repos/saltstack/salt/events","assignees_url":"https://api.github.com/repos/saltstack/salt/assignees{/user}","branches_url":"https://api.github.com/repos/saltstack/salt/branches{/branch}","tags_url":"https://api.github.com/repos/saltstack/salt/tags","blobs_url":"https://api.github.com/repos/saltstack/salt/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/saltstack/salt/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/saltstack/salt/git/refs{/sha}","trees_url":"https://api.github.com/repos/saltstack/salt/git/trees{/sha}","statuses_url":"https://api.github.com/repos/saltstack/salt/statuses/{sha}","languages_url":"https://api.github.com/repos/saltstack/salt/languages","stargazers_url":"https://api.github.com/repos/saltstack/salt/stargazers","contributors_url":"https://api.github.com/repos/saltstack/salt/contributors","subscribers_url":"https://api.github.com/repos/saltstack/salt/subscribers","subscription_url":"https://api.github.com/repos/saltstack/salt/subscription","commits_url":"https://api.github.com/repos/saltstack/salt/commits{/sha}","git_commits_url":"https://api.github.com/repos/saltstack/salt/git/commits{/sha}","comments_url":"https://api.github.com/repos/saltstack/salt/comments{/number}","issue_comment_url":"https://api.github.com/repos/saltstack/salt/issues/comments{/number}","contents_url":"https://api.github.com/repos/saltstack/salt/contents/{+path}","compare_url":"https://api.github.com/repos/saltstack/salt/compare/{base}...{head}","merges_url":"https://api.github.com/repos/saltstack/salt/merges","archive_url":"https://api.github.com/repos/saltstack/salt/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/saltstack/salt/downloads","issues_url":"https://api.github.com/repos/saltstack/salt/issues{/number}","pulls_url":"https://api.github.com/repos/saltstack/salt/pulls{/number}","milestones_url":"https://api.github.com/repos/saltstack/salt/milestones{/number}","notifications_url":"https://api.github.com/repos/saltstack/salt/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/saltstack/salt/labels{/name}","releases_url":"https://api.github.com/repos/saltstack/salt/releases{/id}","created_at":"2011-02-20T20:16:56Z","updated_at":"2015-10-20T10:06:40Z","pushed_at":"2015-10-20T16:12:25Z","git_url":"git://github.com/saltstack/salt.git","ssh_url":"git@github.com:saltstack/salt.git","clone_url":"https://github.com/saltstack/salt.git","svn_url":"https://github.com/saltstack/salt","homepage":"saltstack.com","size":253581,"stargazers_count":5764,"watchers_count":5764,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":2468,"mirror_url":null,"open_issues_count":3027,"forks":2468,"open_issues":3027,"watchers":5764,"default_branch":"develop"}},"_links":{"self":{"href":"https://api.github.com/repos/saltstack/salt/pulls/28089"},"html":{"href":"https://github.com/saltstack/salt/pull/28089"},"issue":{"href":"https://api.github.com/repos/saltstack/salt/issues/28089"},"comments":{"href":"https://api.github.com/repos/saltstack/salt/issues/28089/comments"},"review_comments":{"href":"https://api.github.com/repos/saltstack/salt/pulls/28089/comments"},"review_comment":{"href":"https://api.github.com/repos/saltstack/salt/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/saltstack/salt/pulls/28089/commits"},"statuses":{"href":"https://api.github.com/repos/saltstack/salt/statuses/210101c16c2527b86cfd83a18cd888267bc047b9"}}}}