We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[sensors] `sensor_facts` library is not Python 3 compatible 
**Description**
The `sensor_facts` ansible library treats the output of `Popen.communicate()` as a string but it is now a bytes array in Python 3.

See here for details: https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate

**Steps to reproduce the issue:**
1. Run the `test_sensors` test

**Describe the results you received:**
Test fails during collection of sensor facts from ansible with `TypeError: expected bytes-like object, not str`

**Describe the results you expected:**
Test succeeds. 

**Note: This failure only occurs on images where Python 3 is the default interpreter such as the bullseye builds**


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ansible/library/sonic_release.py`
Content:
```
1 #!/usr/bin/python
2 import subprocess
3 from ansible.module_utils.basic import *
4 
5 DOCUMENTATION = '''
6 ---
7 module: sonic_release
8 version_added: "0.1"
9 author: Ashok Daparthi (ashok.daparthi@dell.com)
10 short_description: Retrive os release facts from device
11 description:
12     - Retrieve sonic release facts for a device, the facts will be
13       inserted to the ansible_facts key.
14 '''
15 
16 EXAMPLES = '''
17 # Gather sonic release facts
18  - name: Gather sonic release
19    sonic_release:
20 
21 '''
22 def main():
23 
24     module = AnsibleModule(argument_spec=dict())
25     """
26     Gets the SONiC OS version that is running on this device.
27     """
28     sonic_release = None
29     sonic_qos_db_fv_reference_with_table = false
30     try:
31         process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'release'],
32                 stdout=subprocess.PIPE, stdin=subprocess.PIPE)
33         self.stdout, stderr = process.communicate()
34         ret_code = process.returncode
35     except Exception as e:
36         module.fail_json(msg=str(e))
37     else:
38         if ret_code != 0:
39              module.fail_json(msg=stderr)
40         else:
41              sonic_release = self.stdout.split('.')[0].strip()
42     """
43     Check for QOS DB format for Field Value refered with tables or not.
44     """
45     old_format_release_list = ["201811", "201911", "202012", "202106"]
46     if any(release == sonic_release for release in old_format_release_list):
47         sonic_qos_db_fv_reference_with_table = true
48 
49     module.exit_json(ansible_facts={'sonic_release': sonic_release, 'sonic_qos_db_fv_reference_with_table': sonic_qos_db_fv_reference_with_table})
50 
51 if __name__ == '__main__':
52     main()
53
```

Path: `ansible/library/sensors_facts.py`
Content:
```
1 #!/usr/bin/python
2 import re
3 import subprocess
4 from ansible.module_utils.basic import *
5 
6 DOCUMENTATION = '''
7 ---
8 module: sensors_facts
9 version_added: "0.2"
10 author: Pavel Shirshov (pavelsh@microsoft.com)
11 short_description: Retrieve sensors facts for a device. Set alarm if there is hardware alarm
12 description:
13     - Checks are defined in ansible variables. Argument for the module is 'checks' with dictionary with parameters.
14     - Retrieved facts will be inserted to the 'sensors' key.
15     - Retrieved raw values will be inserted to the 'raw' key.
16     - Recognized alarms will be inserted to the 'alarms' key.
17     - 'alarm' key will be set to True if the device has any alarm situation.
18     - If there's only one PSU on the device, 'warning' is set to True and 'warnings' have a message about it.
19     - sensors data: group_vars/sonic/sku-sensors/data.yml
20 '''
21 
22 EXAMPLES = '''
23 # Gather sensors facts
24  - name: Gather sensors
25    sensors_facts: checks={{ sensors['Force10-S6000'] }}
26  - name: Output of sensors information
27    debug: var=vars['sensors']
28 
29 '''
30 
31 # Example of the source data
32 '''
33 acpitz-virtual-0
34 temp1:
35   temp1_input: 26.800
36   temp1_crit: 127.000
37 temp2:
38   temp2_input: 26.800
39   temp2_crit: 118.000
40 '''
41 
42 class SensorsModule(object):
43     def __init__(self):
44         self.module = AnsibleModule(
45             argument_spec=dict(
46                 checks=dict(required=True, type='dict'),
47             )
48         )
49 
50         self.checks = self.module.params['checks']
51 
52         self.stdout = None
53         self.skip_devices = set()
54         self.skip_sensors_attr = set()
55         self.raw = {}
56         self.alarms = {}
57         self.warnings = []
58         self.os_version = self._get_os_version()
59         self.facts = {
60             'raw': self.raw,
61             'alarms': self.alarms,
62             'warnings': self.warnings,
63             'alarm': False,
64             'warning': False,
65         }
66 
67         return
68 
69     def _get_os_version(self):
70         """
71         Gets the SONiC OS version that is running on this device.
72         """
73         os_version = None
74         try:
75             process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'build_version'],
76                                        stdout=subprocess.PIPE, stdin=subprocess.PIPE)
77             self.stdout, stderr = process.communicate()
78             ret_code = process.returncode
79         except Exception as e:
80             self.module.fail_json(msg=str(e))
81         else:
82             if ret_code != 0:
83                 self.module.fail_json(msg=stderr)
84             else:
85                 os_version = self.stdout.split('.')[0].strip()
86 
87         return os_version
88 
89     def run(self):
90         '''
91             Main method of the class
92         '''
93         self.collect_sensors()
94         self.parse_sensors()
95         self.psu_check()
96         self.sensor_skip_check()
97         self.check_alarms()
98         self.module.exit_json(ansible_facts={'sensors': self.facts})
99 
100         return
101 
102     def collect_sensors(self):
103         '''
104             Collect sensors by reading output of 'sensors' utility
105         '''
106         try:
107             process = subprocess.Popen(['sensors', '-A', '-u'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
108             self.stdout, stderr = process.communicate()
109             ret_code = process.returncode
110         except Exception as e:
111             self.module.fail_json(msg=str(e))
112         else:
113             if ret_code != 0:
114                 self.module.fail_json(msg=stderr)
115 
116         return
117 
118     def parse_sensors(self):
119         '''
120             Parse 'sensors' utility output into the dictionary self.raw
121         '''
122 
123         # Return true if the row is an empty line
124         is_empty = lambda row: row == ''
125 
126         # Return true if the row is a row which represent device
127         # ('acpitz-virtual-0' in the example above)
128         is_device = lambda row: row[0] != ' ' and row[-1] != ':' and ':' not in row
129 
130         # Return true if the row is a row which represent a subsystem of the device
131         # ('temp1:' in the example above)
132         is_subsystem = lambda row: row[0] != ' ' and row[-1] == ':'
133 
134         # Return true if the row is a row which represent a sensor value
135         # ('temp1_input: 26.800' in the example above)
136         is_sensor = lambda row: row[0] == ' ' and row[-1] != ':' and ':' in row
137 
138         device = None
139         subsystem = None
140         for row in self.stdout.splitlines():
141             if is_empty(row):
142                 continue
143             elif is_device(row):
144                 device = {}
145                 self.raw[row] = device
146             elif is_subsystem(row):
147                 subsystem = {}
148                 device[row[:-1]] = subsystem
149             elif is_sensor(row):
150                 key, value = row.split(':')
151                 subsystem[key.strip()] = value.strip()
152 
153         return
154 
155     def psu_check(self):
156         '''
157             Check that both PSU are presented on the remote system.
158             if it's not true, we set up self.skip_devices set with devices,
159             which should be skipped during checks
160         '''
161 
162         for dev, attrs in self.checks['psu_skips'].items():
163             if dev not in self.raw:
164                 for idev in attrs['skip_list']:
165                     self.skip_devices.add(idev)
166                 self.facts['warning'] = True
167                 self.warnings.append("PSU #%s [%s] is absent" % (attrs['number'], attrs['side']))
168 
169         return
170 
171     def sensor_skip_check(self):
172         """
173         Check that if some sensors need to be skipped on the DUT.
174         If the image version on DUT match, we set up self.skip_sensors_attr set,
175         which should be skipped during checks
176         """
177 
178         for version, attrs in self.checks['sensor_skip_per_version'].items():
179             if version == self.os_version:
180                 for attr in attrs['skip_list']:
181                     self.skip_sensors_attr.add(attr)
182                     self.warnings.append("sensor attributes [%s] is absent in version %s" % (attr, self.os_version))
183                 self.facts['warning'] = True
184 
185         return
186 
187     def get_raw_value(self, path):
188         '''
189             Get value in raw output in the path 'path'
190             Note: Expected path contains two types of value(string and regex)
191                   Regular expression is wrapped with backslash '\'
192         '''
193         keys = path.split('/')
194 
195         cur_values = self.raw
196         res = None
197         for key in keys:
198             if '\\' not in key:
199                 pattern = re.compile(re.escape(key))
200             else:
201                 pattern = re.compile(key.replace('\\', ''))
202             for cur_value in cur_values.keys():
203                 res = re.match(pattern, cur_value)
204                 if res is not None:
205                     cur_values = cur_values[res.group()]
206                     break
207             if res is None:
208                 return None
209 
210         return cur_values
211 
212     def skip_the_value(self, path):
213         """
214         Check if the path has been added to self.skip_devices of self.skip_sensors_attr.
215         If yes, this path shall be skipped in the alarm check.
216         """
217         if path.split('/')[0] in self.skip_devices or path in self.skip_sensors_attr:
218             return True
219         return False
220 
221     def check_alarms(self):
222         """
223         Calculate alarm situation using the lists
224         """
225         # check alarm lists
226         for hw_part, alarm_list in self.checks['alarms'].items():
227             reasons = '%s_reasons' % hw_part
228             self.alarms[hw_part] = False
229             self.alarms[reasons] = []
230             for path in alarm_list:
231                 if self.skip_the_value(path):
232                     continue
233                 value = self.get_raw_value(path)
234                 if value is None:
235                     self.alarms[hw_part] = True
236                     self.facts['alarm'] = True
237                     self.alarms[reasons].append('Path %s is not exist' % path)
238                 elif value != '0.000':
239                     self.alarms[hw_part] = True
240                     self.facts['alarm'] = True
241                     self.alarms[reasons].append('Alarm on %s' % path)
242 
243         # check compare lists
244         for hw_part, compare_list in self.checks['compares'].items():
245             reasons = '%s_reasons' % hw_part
246             for (path_input, path_max) in compare_list:
247                 if self.skip_the_value(path_input):
248                     continue
249                 value_input = self.get_raw_value(path_input)
250                 value_max = self.get_raw_value(path_max)
251                 if value_input is None:
252                     self.alarms[hw_part] = True
253                     self.facts['alarm'] = True
254                     self.alarms[reasons].append('Path %s is not exist' % path_input)
255                 elif value_max is None:
256                     self.alarms[hw_part] = True
257                     self.facts['alarm'] = True
258                     self.alarms[reasons].append('Path %s is not exist' % path_max)
259                 elif float(value_input) >= float(value_max) :
260                     self.alarms[hw_part] = True
261                     self.facts['alarm'] = True
262                     self.alarms[reasons].append('Alarm on %s' % path_input)
263 
264         # check not zero lists
265         for hw_part, not_zero_list in self.checks['non_zero'].items():
266             reasons = '%s_reasons' % hw_part
267             for path in not_zero_list:
268                 if self.skip_the_value(path):
269                     continue
270                 value = self.get_raw_value(path)
271                 if value is None:
272                     self.alarms[hw_part] = True
273                     self.facts['alarm'] = True
274                     self.alarms[reasons].append('Path %s is not exist' % path)
275                 elif value == '0.000':
276                     self.alarms[hw_part] = True
277                     self.facts['alarm'] = True
278                     self.alarms[reasons].append('Alarm on %s' % path)
279 
280         return
281 
282 def main():
283     sensors = SensorsModule()
284     sensors.run()
285 
286     return
287 
288 if __name__ == '__main__':
289     main()
290
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/ansible/library/sensors_facts.py b/ansible/library/sensors_facts.py
--- a/ansible/library/sensors_facts.py
+++ b/ansible/library/sensors_facts.py
@@ -82,7 +82,7 @@
             if ret_code != 0:
                 self.module.fail_json(msg=stderr)
             else:
-                os_version = self.stdout.split('.')[0].strip()
+                os_version = self.stdout.decode('utf-8').split('.')[0].strip()
 
         return os_version
 
@@ -106,6 +106,8 @@
         try:
             process = subprocess.Popen(['sensors', '-A', '-u'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)
             self.stdout, stderr = process.communicate()
+            self.stdout = self.stdout.decode('utf-8')
+            stderr = stderr.decode('utf-8')
             ret_code = process.returncode
         except Exception as e:
             self.module.fail_json(msg=str(e))
diff --git a/ansible/library/sonic_release.py b/ansible/library/sonic_release.py
--- a/ansible/library/sonic_release.py
+++ b/ansible/library/sonic_release.py
@@ -31,6 +31,8 @@
         process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'release'],
                 stdout=subprocess.PIPE, stdin=subprocess.PIPE)
         self.stdout, stderr = process.communicate()
+        self.stdout = self.stdout.decode('utf-8')
+        stderr = stderr.decode('utf-8')
         ret_code = process.returncode
     except Exception as e:
         module.fail_json(msg=str(e))

{"golden_diff": "diff --git a/ansible/library/sensors_facts.py b/ansible/library/sensors_facts.py\n--- a/ansible/library/sensors_facts.py\n+++ b/ansible/library/sensors_facts.py\n@@ -82,7 +82,7 @@\n             if ret_code != 0:\n                 self.module.fail_json(msg=stderr)\n             else:\n-                os_version = self.stdout.split('.')[0].strip()\n+                os_version = self.stdout.decode('utf-8').split('.')[0].strip()\n \n         return os_version\n \n@@ -106,6 +106,8 @@\n         try:\n             process = subprocess.Popen(['sensors', '-A', '-u'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n             self.stdout, stderr = process.communicate()\n+            self.stdout = self.stdout.decode('utf-8')\n+            stderr = stderr.decode('utf-8')\n             ret_code = process.returncode\n         except Exception as e:\n             self.module.fail_json(msg=str(e))\ndiff --git a/ansible/library/sonic_release.py b/ansible/library/sonic_release.py\n--- a/ansible/library/sonic_release.py\n+++ b/ansible/library/sonic_release.py\n@@ -31,6 +31,8 @@\n         process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'release'],\n                 stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n         self.stdout, stderr = process.communicate()\n+        self.stdout = self.stdout.decode('utf-8')\n+        stderr = stderr.decode('utf-8')\n         ret_code = process.returncode\n     except Exception as e:\n         module.fail_json(msg=str(e))\n", "issue": "[sensors] `sensor_facts` library is not Python 3 compatible \n**Description**\r\nThe `sensor_facts` ansible library treats the output of `Popen.communicate()` as a string but it is now a bytes array in Python 3.\r\n\r\nSee here for details: https://docs.python.org/3/library/subprocess.html#subprocess.Popen.communicate\r\n\r\n**Steps to reproduce the issue:**\r\n1. Run the `test_sensors` test\r\n\r\n**Describe the results you received:**\r\nTest fails during collection of sensor facts from ansible with `TypeError: expected bytes-like object, not str`\r\n\r\n**Describe the results you expected:**\r\nTest succeeds. \r\n\r\n**Note: This failure only occurs on images where Python 3 is the default interpreter such as the bullseye builds**\r\n\n", "before_files": [{"content": "#!/usr/bin/python\nimport subprocess\nfrom ansible.module_utils.basic import *\n\nDOCUMENTATION = '''\n---\nmodule: sonic_release\nversion_added: \"0.1\"\nauthor: Ashok Daparthi (ashok.daparthi@dell.com)\nshort_description: Retrive os release facts from device\ndescription:\n    - Retrieve sonic release facts for a device, the facts will be\n      inserted to the ansible_facts key.\n'''\n\nEXAMPLES = '''\n# Gather sonic release facts\n - name: Gather sonic release\n   sonic_release:\n\n'''\ndef main():\n\n    module = AnsibleModule(argument_spec=dict())\n    \"\"\"\n    Gets the SONiC OS version that is running on this device.\n    \"\"\"\n    sonic_release = None\n    sonic_qos_db_fv_reference_with_table = false\n    try:\n        process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'release'],\n                stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n        self.stdout, stderr = process.communicate()\n        ret_code = process.returncode\n    except Exception as e:\n        module.fail_json(msg=str(e))\n    else:\n        if ret_code != 0:\n             module.fail_json(msg=stderr)\n        else:\n             sonic_release = self.stdout.split('.')[0].strip()\n    \"\"\"\n    Check for QOS DB format for Field Value refered with tables or not.\n    \"\"\"\n    old_format_release_list = [\"201811\", \"201911\", \"202012\", \"202106\"]\n    if any(release == sonic_release for release in old_format_release_list):\n        sonic_qos_db_fv_reference_with_table = true\n\n    module.exit_json(ansible_facts={'sonic_release': sonic_release, 'sonic_qos_db_fv_reference_with_table': sonic_qos_db_fv_reference_with_table})\n\nif __name__ == '__main__':\n    main()\n", "path": "ansible/library/sonic_release.py"}, {"content": "#!/usr/bin/python\nimport re\nimport subprocess\nfrom ansible.module_utils.basic import *\n\nDOCUMENTATION = '''\n---\nmodule: sensors_facts\nversion_added: \"0.2\"\nauthor: Pavel Shirshov (pavelsh@microsoft.com)\nshort_description: Retrieve sensors facts for a device. Set alarm if there is hardware alarm\ndescription:\n    - Checks are defined in ansible variables. Argument for the module is 'checks' with dictionary with parameters.\n    - Retrieved facts will be inserted to the 'sensors' key.\n    - Retrieved raw values will be inserted to the 'raw' key.\n    - Recognized alarms will be inserted to the 'alarms' key.\n    - 'alarm' key will be set to True if the device has any alarm situation.\n    - If there's only one PSU on the device, 'warning' is set to True and 'warnings' have a message about it.\n    - sensors data: group_vars/sonic/sku-sensors/data.yml\n'''\n\nEXAMPLES = '''\n# Gather sensors facts\n - name: Gather sensors\n   sensors_facts: checks={{ sensors['Force10-S6000'] }}\n - name: Output of sensors information\n   debug: var=vars['sensors']\n\n'''\n\n# Example of the source data\n'''\nacpitz-virtual-0\ntemp1:\n  temp1_input: 26.800\n  temp1_crit: 127.000\ntemp2:\n  temp2_input: 26.800\n  temp2_crit: 118.000\n'''\n\nclass SensorsModule(object):\n    def __init__(self):\n        self.module = AnsibleModule(\n            argument_spec=dict(\n                checks=dict(required=True, type='dict'),\n            )\n        )\n\n        self.checks = self.module.params['checks']\n\n        self.stdout = None\n        self.skip_devices = set()\n        self.skip_sensors_attr = set()\n        self.raw = {}\n        self.alarms = {}\n        self.warnings = []\n        self.os_version = self._get_os_version()\n        self.facts = {\n            'raw': self.raw,\n            'alarms': self.alarms,\n            'warnings': self.warnings,\n            'alarm': False,\n            'warning': False,\n        }\n\n        return\n\n    def _get_os_version(self):\n        \"\"\"\n        Gets the SONiC OS version that is running on this device.\n        \"\"\"\n        os_version = None\n        try:\n            process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'build_version'],\n                                       stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n            self.stdout, stderr = process.communicate()\n            ret_code = process.returncode\n        except Exception as e:\n            self.module.fail_json(msg=str(e))\n        else:\n            if ret_code != 0:\n                self.module.fail_json(msg=stderr)\n            else:\n                os_version = self.stdout.split('.')[0].strip()\n\n        return os_version\n\n    def run(self):\n        '''\n            Main method of the class\n        '''\n        self.collect_sensors()\n        self.parse_sensors()\n        self.psu_check()\n        self.sensor_skip_check()\n        self.check_alarms()\n        self.module.exit_json(ansible_facts={'sensors': self.facts})\n\n        return\n\n    def collect_sensors(self):\n        '''\n            Collect sensors by reading output of 'sensors' utility\n        '''\n        try:\n            process = subprocess.Popen(['sensors', '-A', '-u'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n            self.stdout, stderr = process.communicate()\n            ret_code = process.returncode\n        except Exception as e:\n            self.module.fail_json(msg=str(e))\n        else:\n            if ret_code != 0:\n                self.module.fail_json(msg=stderr)\n\n        return\n\n    def parse_sensors(self):\n        '''\n            Parse 'sensors' utility output into the dictionary self.raw\n        '''\n\n        # Return true if the row is an empty line\n        is_empty = lambda row: row == ''\n\n        # Return true if the row is a row which represent device\n        # ('acpitz-virtual-0' in the example above)\n        is_device = lambda row: row[0] != ' ' and row[-1] != ':' and ':' not in row\n\n        # Return true if the row is a row which represent a subsystem of the device\n        # ('temp1:' in the example above)\n        is_subsystem = lambda row: row[0] != ' ' and row[-1] == ':'\n\n        # Return true if the row is a row which represent a sensor value\n        # ('temp1_input: 26.800' in the example above)\n        is_sensor = lambda row: row[0] == ' ' and row[-1] != ':' and ':' in row\n\n        device = None\n        subsystem = None\n        for row in self.stdout.splitlines():\n            if is_empty(row):\n                continue\n            elif is_device(row):\n                device = {}\n                self.raw[row] = device\n            elif is_subsystem(row):\n                subsystem = {}\n                device[row[:-1]] = subsystem\n            elif is_sensor(row):\n                key, value = row.split(':')\n                subsystem[key.strip()] = value.strip()\n\n        return\n\n    def psu_check(self):\n        '''\n            Check that both PSU are presented on the remote system.\n            if it's not true, we set up self.skip_devices set with devices,\n            which should be skipped during checks\n        '''\n\n        for dev, attrs in self.checks['psu_skips'].items():\n            if dev not in self.raw:\n                for idev in attrs['skip_list']:\n                    self.skip_devices.add(idev)\n                self.facts['warning'] = True\n                self.warnings.append(\"PSU #%s [%s] is absent\" % (attrs['number'], attrs['side']))\n\n        return\n\n    def sensor_skip_check(self):\n        \"\"\"\n        Check that if some sensors need to be skipped on the DUT.\n        If the image version on DUT match, we set up self.skip_sensors_attr set,\n        which should be skipped during checks\n        \"\"\"\n\n        for version, attrs in self.checks['sensor_skip_per_version'].items():\n            if version == self.os_version:\n                for attr in attrs['skip_list']:\n                    self.skip_sensors_attr.add(attr)\n                    self.warnings.append(\"sensor attributes [%s] is absent in version %s\" % (attr, self.os_version))\n                self.facts['warning'] = True\n\n        return\n\n    def get_raw_value(self, path):\n        '''\n            Get value in raw output in the path 'path'\n            Note: Expected path contains two types of value(string and regex)\n                  Regular expression is wrapped with backslash '\\'\n        '''\n        keys = path.split('/')\n\n        cur_values = self.raw\n        res = None\n        for key in keys:\n            if '\\\\' not in key:\n                pattern = re.compile(re.escape(key))\n            else:\n                pattern = re.compile(key.replace('\\\\', ''))\n            for cur_value in cur_values.keys():\n                res = re.match(pattern, cur_value)\n                if res is not None:\n                    cur_values = cur_values[res.group()]\n                    break\n            if res is None:\n                return None\n\n        return cur_values\n\n    def skip_the_value(self, path):\n        \"\"\"\n        Check if the path has been added to self.skip_devices of self.skip_sensors_attr.\n        If yes, this path shall be skipped in the alarm check.\n        \"\"\"\n        if path.split('/')[0] in self.skip_devices or path in self.skip_sensors_attr:\n            return True\n        return False\n\n    def check_alarms(self):\n        \"\"\"\n        Calculate alarm situation using the lists\n        \"\"\"\n        # check alarm lists\n        for hw_part, alarm_list in self.checks['alarms'].items():\n            reasons = '%s_reasons' % hw_part\n            self.alarms[hw_part] = False\n            self.alarms[reasons] = []\n            for path in alarm_list:\n                if self.skip_the_value(path):\n                    continue\n                value = self.get_raw_value(path)\n                if value is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path)\n                elif value != '0.000':\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Alarm on %s' % path)\n\n        # check compare lists\n        for hw_part, compare_list in self.checks['compares'].items():\n            reasons = '%s_reasons' % hw_part\n            for (path_input, path_max) in compare_list:\n                if self.skip_the_value(path_input):\n                    continue\n                value_input = self.get_raw_value(path_input)\n                value_max = self.get_raw_value(path_max)\n                if value_input is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path_input)\n                elif value_max is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path_max)\n                elif float(value_input) >= float(value_max) :\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Alarm on %s' % path_input)\n\n        # check not zero lists\n        for hw_part, not_zero_list in self.checks['non_zero'].items():\n            reasons = '%s_reasons' % hw_part\n            for path in not_zero_list:\n                if self.skip_the_value(path):\n                    continue\n                value = self.get_raw_value(path)\n                if value is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path)\n                elif value == '0.000':\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Alarm on %s' % path)\n\n        return\n\ndef main():\n    sensors = SensorsModule()\n    sensors.run()\n\n    return\n\nif __name__ == '__main__':\n    main()\n", "path": "ansible/library/sensors_facts.py"}], "after_files": [{"content": "#!/usr/bin/python\nimport subprocess\nfrom ansible.module_utils.basic import *\n\nDOCUMENTATION = '''\n---\nmodule: sonic_release\nversion_added: \"0.1\"\nauthor: Ashok Daparthi (ashok.daparthi@dell.com)\nshort_description: Retrive os release facts from device\ndescription:\n    - Retrieve sonic release facts for a device, the facts will be\n      inserted to the ansible_facts key.\n'''\n\nEXAMPLES = '''\n# Gather sonic release facts\n - name: Gather sonic release\n   sonic_release:\n\n'''\ndef main():\n\n    module = AnsibleModule(argument_spec=dict())\n    \"\"\"\n    Gets the SONiC OS version that is running on this device.\n    \"\"\"\n    sonic_release = None\n    sonic_qos_db_fv_reference_with_table = false\n    try:\n        process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'release'],\n                stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n        self.stdout, stderr = process.communicate()\n        self.stdout = self.stdout.decode('utf-8')\n        stderr = stderr.decode('utf-8')\n        ret_code = process.returncode\n    except Exception as e:\n        module.fail_json(msg=str(e))\n    else:\n        if ret_code != 0:\n             module.fail_json(msg=stderr)\n        else:\n             sonic_release = self.stdout.split('.')[0].strip()\n    \"\"\"\n    Check for QOS DB format for Field Value refered with tables or not.\n    \"\"\"\n    old_format_release_list = [\"201811\", \"201911\", \"202012\", \"202106\"]\n    if any(release == sonic_release for release in old_format_release_list):\n        sonic_qos_db_fv_reference_with_table = true\n\n    module.exit_json(ansible_facts={'sonic_release': sonic_release, 'sonic_qos_db_fv_reference_with_table': sonic_qos_db_fv_reference_with_table})\n\nif __name__ == '__main__':\n    main()\n", "path": "ansible/library/sonic_release.py"}, {"content": "#!/usr/bin/python\nimport re\nimport subprocess\nfrom ansible.module_utils.basic import *\n\nDOCUMENTATION = '''\n---\nmodule: sensors_facts\nversion_added: \"0.2\"\nauthor: Pavel Shirshov (pavelsh@microsoft.com)\nshort_description: Retrieve sensors facts for a device. Set alarm if there is hardware alarm\ndescription:\n    - Checks are defined in ansible variables. Argument for the module is 'checks' with dictionary with parameters.\n    - Retrieved facts will be inserted to the 'sensors' key.\n    - Retrieved raw values will be inserted to the 'raw' key.\n    - Recognized alarms will be inserted to the 'alarms' key.\n    - 'alarm' key will be set to True if the device has any alarm situation.\n    - If there's only one PSU on the device, 'warning' is set to True and 'warnings' have a message about it.\n    - sensors data: group_vars/sonic/sku-sensors/data.yml\n'''\n\nEXAMPLES = '''\n# Gather sensors facts\n - name: Gather sensors\n   sensors_facts: checks={{ sensors['Force10-S6000'] }}\n - name: Output of sensors information\n   debug: var=vars['sensors']\n\n'''\n\n# Example of the source data\n'''\nacpitz-virtual-0\ntemp1:\n  temp1_input: 26.800\n  temp1_crit: 127.000\ntemp2:\n  temp2_input: 26.800\n  temp2_crit: 118.000\n'''\n\nclass SensorsModule(object):\n    def __init__(self):\n        self.module = AnsibleModule(\n            argument_spec=dict(\n                checks=dict(required=True, type='dict'),\n            )\n        )\n\n        self.checks = self.module.params['checks']\n\n        self.stdout = None\n        self.skip_devices = set()\n        self.skip_sensors_attr = set()\n        self.raw = {}\n        self.alarms = {}\n        self.warnings = []\n        self.os_version = self._get_os_version()\n        self.facts = {\n            'raw': self.raw,\n            'alarms': self.alarms,\n            'warnings': self.warnings,\n            'alarm': False,\n            'warning': False,\n        }\n\n        return\n\n    def _get_os_version(self):\n        \"\"\"\n        Gets the SONiC OS version that is running on this device.\n        \"\"\"\n        os_version = None\n        try:\n            process = subprocess.Popen(['sonic-cfggen', '-y', '/etc/sonic/sonic_version.yml', '-v', 'build_version'],\n                                       stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n            self.stdout, stderr = process.communicate()\n            ret_code = process.returncode\n        except Exception as e:\n            self.module.fail_json(msg=str(e))\n        else:\n            if ret_code != 0:\n                self.module.fail_json(msg=stderr)\n            else:\n                os_version = self.stdout.decode('utf-8').split('.')[0].strip()\n\n        return os_version\n\n    def run(self):\n        '''\n            Main method of the class\n        '''\n        self.collect_sensors()\n        self.parse_sensors()\n        self.psu_check()\n        self.sensor_skip_check()\n        self.check_alarms()\n        self.module.exit_json(ansible_facts={'sensors': self.facts})\n\n        return\n\n    def collect_sensors(self):\n        '''\n            Collect sensors by reading output of 'sensors' utility\n        '''\n        try:\n            process = subprocess.Popen(['sensors', '-A', '-u'], stdout=subprocess.PIPE, stdin=subprocess.PIPE)\n            self.stdout, stderr = process.communicate()\n            self.stdout = self.stdout.decode('utf-8')\n            stderr = stderr.decode('utf-8')\n            ret_code = process.returncode\n        except Exception as e:\n            self.module.fail_json(msg=str(e))\n        else:\n            if ret_code != 0:\n                self.module.fail_json(msg=stderr)\n\n        return\n\n    def parse_sensors(self):\n        '''\n            Parse 'sensors' utility output into the dictionary self.raw\n        '''\n\n        # Return true if the row is an empty line\n        is_empty = lambda row: row == ''\n\n        # Return true if the row is a row which represent device\n        # ('acpitz-virtual-0' in the example above)\n        is_device = lambda row: row[0] != ' ' and row[-1] != ':' and ':' not in row\n\n        # Return true if the row is a row which represent a subsystem of the device\n        # ('temp1:' in the example above)\n        is_subsystem = lambda row: row[0] != ' ' and row[-1] == ':'\n\n        # Return true if the row is a row which represent a sensor value\n        # ('temp1_input: 26.800' in the example above)\n        is_sensor = lambda row: row[0] == ' ' and row[-1] != ':' and ':' in row\n\n        device = None\n        subsystem = None\n        for row in self.stdout.splitlines():\n            if is_empty(row):\n                continue\n            elif is_device(row):\n                device = {}\n                self.raw[row] = device\n            elif is_subsystem(row):\n                subsystem = {}\n                device[row[:-1]] = subsystem\n            elif is_sensor(row):\n                key, value = row.split(':')\n                subsystem[key.strip()] = value.strip()\n\n        return\n\n    def psu_check(self):\n        '''\n            Check that both PSU are presented on the remote system.\n            if it's not true, we set up self.skip_devices set with devices,\n            which should be skipped during checks\n        '''\n\n        for dev, attrs in self.checks['psu_skips'].items():\n            if dev not in self.raw:\n                for idev in attrs['skip_list']:\n                    self.skip_devices.add(idev)\n                self.facts['warning'] = True\n                self.warnings.append(\"PSU #%s [%s] is absent\" % (attrs['number'], attrs['side']))\n\n        return\n\n    def sensor_skip_check(self):\n        \"\"\"\n        Check that if some sensors need to be skipped on the DUT.\n        If the image version on DUT match, we set up self.skip_sensors_attr set,\n        which should be skipped during checks\n        \"\"\"\n\n        for version, attrs in self.checks['sensor_skip_per_version'].items():\n            if version == self.os_version:\n                for attr in attrs['skip_list']:\n                    self.skip_sensors_attr.add(attr)\n                    self.warnings.append(\"sensor attributes [%s] is absent in version %s\" % (attr, self.os_version))\n                self.facts['warning'] = True\n\n        return\n\n    def get_raw_value(self, path):\n        '''\n            Get value in raw output in the path 'path'\n            Note: Expected path contains two types of value(string and regex)\n                  Regular expression is wrapped with backslash '\\'\n        '''\n        keys = path.split('/')\n\n        cur_values = self.raw\n        res = None\n        for key in keys:\n            if '\\\\' not in key:\n                pattern = re.compile(re.escape(key))\n            else:\n                pattern = re.compile(key.replace('\\\\', ''))\n            for cur_value in cur_values.keys():\n                res = re.match(pattern, cur_value)\n                if res is not None:\n                    cur_values = cur_values[res.group()]\n                    break\n            if res is None:\n                return None\n\n        return cur_values\n\n    def skip_the_value(self, path):\n        \"\"\"\n        Check if the path has been added to self.skip_devices of self.skip_sensors_attr.\n        If yes, this path shall be skipped in the alarm check.\n        \"\"\"\n        if path.split('/')[0] in self.skip_devices or path in self.skip_sensors_attr:\n            return True\n        return False\n\n    def check_alarms(self):\n        \"\"\"\n        Calculate alarm situation using the lists\n        \"\"\"\n        # check alarm lists\n        for hw_part, alarm_list in self.checks['alarms'].items():\n            reasons = '%s_reasons' % hw_part\n            self.alarms[hw_part] = False\n            self.alarms[reasons] = []\n            for path in alarm_list:\n                if self.skip_the_value(path):\n                    continue\n                value = self.get_raw_value(path)\n                if value is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path)\n                elif value != '0.000':\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Alarm on %s' % path)\n\n        # check compare lists\n        for hw_part, compare_list in self.checks['compares'].items():\n            reasons = '%s_reasons' % hw_part\n            for (path_input, path_max) in compare_list:\n                if self.skip_the_value(path_input):\n                    continue\n                value_input = self.get_raw_value(path_input)\n                value_max = self.get_raw_value(path_max)\n                if value_input is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path_input)\n                elif value_max is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path_max)\n                elif float(value_input) >= float(value_max) :\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Alarm on %s' % path_input)\n\n        # check not zero lists\n        for hw_part, not_zero_list in self.checks['non_zero'].items():\n            reasons = '%s_reasons' % hw_part\n            for path in not_zero_list:\n                if self.skip_the_value(path):\n                    continue\n                value = self.get_raw_value(path)\n                if value is None:\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Path %s is not exist' % path)\n                elif value == '0.000':\n                    self.alarms[hw_part] = True\n                    self.facts['alarm'] = True\n                    self.alarms[reasons].append('Alarm on %s' % path)\n\n        return\n\ndef main():\n    sensors = SensorsModule()\n    sensors.run()\n\n    return\n\nif __name__ == '__main__':\n    main()\n", "path": "ansible/library/sensors_facts.py"}]}