You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by ao...@apache.org on 2016/09/26 10:35:55 UTC

[2/2] ambari git commit: AMBARI-18453. Ignore mounts that are read-only (aonishuk)

AMBARI-18453. Ignore mounts that are read-only (aonishuk)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/f5f1272b
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/f5f1272b
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/f5f1272b

Branch: refs/heads/branch-2.5
Commit: f5f1272b8a04f0a18c8252b9d60ac1620bc91d51
Parents: 7d8adc4
Author: Andrew Onishuk <ao...@hortonworks.com>
Authored: Mon Sep 26 13:35:53 2016 +0300
Committer: Andrew Onishuk <ao...@hortonworks.com>
Committed: Mon Sep 26 13:35:53 2016 +0300

----------------------------------------------------------------------
 .../src/main/python/ambari_agent/Facter.py      |  82 +++++----
 .../src/main/python/ambari_agent/Hardware.py    | 165 ++++++++++-------
 .../test/python/ambari_agent/TestController.py  |   4 +-
 .../test/python/ambari_agent/TestHardware.py    | 177 ++++++++++++-------
 .../test/python/ambari_agent/TestHeartbeat.py   |   6 +-
 .../python/ambari_agent/TestRegistration.py     |   2 +-
 6 files changed, 270 insertions(+), 166 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/f5f1272b/ambari-agent/src/main/python/ambari_agent/Facter.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/Facter.py b/ambari-agent/src/main/python/ambari_agent/Facter.py
index 7520f4c..a1f815d 100644
--- a/ambari-agent/src/main/python/ambari_agent/Facter.py
+++ b/ambari-agent/src/main/python/ambari_agent/Facter.py
@@ -196,41 +196,57 @@ class Facter(object):
         log.info("'system_resource_dir' is not set - it won't be used for gathering system resources.")
     return systemResources
 
+  def getFqdn(self):
+    raise NotImplementedError()
 
-  def facterInfo(self):
-    facterInfo = {}
-    facterInfo['id'] = self.getId()
-    facterInfo['kernel'] = self.getKernel()
-    facterInfo['domain'] = self.getDomain()
-    facterInfo['fqdn'] = self.getFqdn()
-    facterInfo['hostname'] = self.getHostname()
-    facterInfo['macaddress'] = self.getMacAddress()
-    facterInfo['architecture'] = self.getArchitecture()
-    facterInfo['operatingsystem'] = self.getOperatingSystem()
-    facterInfo['operatingsystemrelease'] = self.getOperatingSystemRelease()
-    facterInfo['physicalprocessorcount'] = self.getProcessorcount()
-    facterInfo['processorcount'] = self.getProcessorcount()
-    facterInfo['timezone'] = self.getTimeZone()
-    facterInfo['hardwareisa'] = self.getArchitecture()
-    facterInfo['hardwaremodel'] = self.getArchitecture()
-    facterInfo['kernelrelease'] = self.getKernelRelease()
-    facterInfo['kernelversion'] = self.getKernelVersion()
-    facterInfo['osfamily'] = self.getOsFamily()
-    facterInfo['kernelmajversion'] = self.getKernelMajVersion()
-
-    facterInfo['ipaddress'] = self.getIpAddress()
-    facterInfo['netmask'] = self.getNetmask()
-    facterInfo['interfaces'] = self.getInterfaces()
-
-    facterInfo['uptime_seconds'] = str(self.getUptimeSeconds())
-    facterInfo['uptime_hours'] = str(self.getUptimeHours())
-    facterInfo['uptime_days'] = str(self.getUptimeDays())
-
-    facterInfo['memorysize'] = self.getMemorySize()
-    facterInfo['memoryfree'] = self.getMemoryFree()
-    facterInfo['memorytotal'] = self.getMemoryTotal()
+  def getNetmask(self):
+    raise NotImplementedError()
 
-    return facterInfo
+  def getInterfaces(self):
+    raise NotImplementedError()
+
+  def getUptimeSeconds(self):
+    raise NotImplementedError()
+
+  def getMemorySize(self):
+    raise NotImplementedError()
+
+  def getMemoryFree(self):
+    raise NotImplementedError()
+
+  def getMemoryTotal(self):
+    raise NotImplementedError()
+
+  def facterInfo(self):
+    return {
+      'id': self.getId(),
+      'kernel': self.getKernel(),
+      'domain': self.getDomain(),
+      'fqdn': self.getFqdn(),
+      'hostname': self.getHostname(),
+      'macaddress': self.getMacAddress(),
+      'architecture': self.getArchitecture(),
+      'operatingsystem': self.getOperatingSystem(),
+      'operatingsystemrelease': self.getOperatingSystemRelease(),
+      'physicalprocessorcount': self.getProcessorcount(),
+      'processorcount': self.getProcessorcount(),
+      'timezone': self.getTimeZone(),
+      'hardwareisa': self.getArchitecture(),
+      'hardwaremodel': self.getArchitecture(),
+      'kernelrelease': self.getKernelRelease(),
+      'kernelversion': self.getKernelVersion(),
+      'osfamily': self.getOsFamily(),
+      'kernelmajversion': self.getKernelMajVersion(),
+      'ipaddress': self.getIpAddress(),
+      'netmask': self.getNetmask(),
+      'interfaces': self.getInterfaces(),
+      'uptime_seconds': str(self.getUptimeSeconds()),
+      'uptime_hours': str(self.getUptimeHours()),
+      'uptime_days': str(self.getUptimeDays()),
+      'memorysize': self.getMemorySize(),
+      'memoryfree': self.getMemoryFree(),
+      'memorytotal': self.getMemoryTotal()
+    }
 
   #Convert kB to GB
   @staticmethod

http://git-wip-us.apache.org/repos/asf/ambari/blob/f5f1272b/ambari-agent/src/main/python/ambari_agent/Hardware.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/main/python/ambari_agent/Hardware.py b/ambari-agent/src/main/python/ambari_agent/Hardware.py
index 3ab7700..a65c1cd 100644
--- a/ambari-agent/src/main/python/ambari_agent/Hardware.py
+++ b/ambari-agent/src/main/python/ambari_agent/Hardware.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-'''
+"""
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
@@ -16,100 +16,134 @@ distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-'''
+"""
 
 import os.path
 import logging
 import subprocess
 from resource_management.core.shell import call
-from resource_management.core.exceptions import ExecuteTimeoutException
-from ambari_commons.constants import AMBARI_SUDO_BINARY
+from resource_management.core.exceptions import ExecuteTimeoutException, Fail
 from ambari_commons.shell import shellRunner
 from Facter import Facter
 from ambari_commons.os_check import OSConst
 from ambari_commons.os_family_impl import OsFamilyFuncImpl, OsFamilyImpl
 from AmbariConfig import AmbariConfig
+from resource_management.core.sudo import path_isfile
+
 logger = logging.getLogger()
 
+
 class Hardware:
   SSH_KEY_PATTERN = 'ssh.*key'
   WINDOWS_GET_DRIVES_CMD = "foreach ($drive in [System.IO.DriveInfo]::getdrives()){$available = $drive.TotalFreeSpace;$used = $drive.TotalSize-$drive.TotalFreeSpace;$percent = ($used*100)/$drive.TotalSize;$size = $drive.TotalSize;$type = $drive.DriveFormat;$mountpoint = $drive.RootDirectory.FullName;echo \"$available $used $percent% $size $type $mountpoint\"}"
   CHECK_REMOTE_MOUNTS_KEY = 'agent.check.remote.mounts'
   CHECK_REMOTE_MOUNTS_TIMEOUT_KEY = 'agent.check.mounts.timeout'
   CHECK_REMOTE_MOUNTS_TIMEOUT_DEFAULT = '10'
+  IGNORE_ROOT_MOUNTS = ["proc", "dev", "sys"]
+  IGNORE_DEVICES = ["proc", "tmpfs", "cgroup", "mqueue", "shm"]
 
   def __init__(self):
-    self.hardware = {}
-    self.hardware['mounts'] = Hardware.osdisks()
+    self.hardware = {
+      'mounts': Hardware.osdisks()
+    }
     self.hardware.update(Facter().facterInfo())
-    pass
 
-  @staticmethod
-  def extractMountInfo(outputLine):
-    if outputLine == None or len(outputLine) == 0:
-      return None
+  @classmethod
+  def _parse_df_line(cls, line):
+    """
+      Initialize data-structure from string in specific 'df' command output format
 
-      """ this ignores any spaces in the filesystemname and mounts """
-    split = outputLine.split()
-    if (len(split)) == 7:
-      device, type, size, used, available, percent, mountpoint = split
-      mountinfo = {
-        'size': size,
-        'used': used,
-        'available': available,
-        'percent': percent,
-        'mountpoint': mountpoint,
-        'type': type,
-        'device': device}
-      return mountinfo
-    else:
+      Expected string format:
+       device fs_type disk_size used_size available_size capacity_used_percents mount_point
+
+    :type line str
+    """
+
+    line_split = line.split()
+    if len(line_split) != 7:
       return None
 
-  @staticmethod
+    titles = ["device", "type", "size", "used", "available", "percent", "mountpoint"]
+    return dict(zip(titles, line_split))
+
+  @classmethod
+  def _get_mount_check_timeout(cls, config=None):
+    """Return timeout for df call command"""
+    if config and config.has_option(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY) \
+      and config.get(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY) != "0":
+
+      return config.get(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY)
+
+    return Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_DEFAULT
+
+  @classmethod
+  def _check_remote_mounts(cls, config=None):
+    """Verify if remote mount allowed to be processed or not"""
+    if config and config.has_option(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_KEY) and \
+       config.get(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_KEY).lower() == "false":
+
+      return False
+
+    return True
+
+  @classmethod
   @OsFamilyFuncImpl(OsFamilyImpl.DEFAULT)
-  def osdisks(config = None):
+  def osdisks(cls, config=None):
     """ Run df to find out the disks on the host. Only works on linux
     platforms. Note that this parser ignores any filesystems with spaces
     and any mounts with spaces. """
-    mounts = []
-    command = []
-    timeout = Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_DEFAULT
-    if config and \
-        config.has_option(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY) and \
-        config.get(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY) != "0":
-        timeout = config.get(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY)
-    command.append("timeout")
-    command.append(timeout)
-    command.append("df")
-    command.append("-kPT")
-    if config and \
-        config.has_option(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_KEY) and \
-        config.get(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_KEY).lower() == "false":
-      #limit listing to local file systems
+    timeout = cls._get_mount_check_timeout(config)
+    command = ["timeout", timeout, "df", "-kPT"]
+
+    if not cls._check_remote_mounts(config):
       command.append("-l")
 
     df = subprocess.Popen(command, stdout=subprocess.PIPE)
     dfdata = df.communicate()[0]
-    lines = dfdata.splitlines()
-    for l in lines:
-      mountinfo = Hardware.extractMountInfo(l)
-      if mountinfo != None and Hardware._chk_mount(mountinfo['mountpoint']):
-        mounts.append(mountinfo)
-      pass
-    pass
-    return mounts
+    mounts = [cls._parse_df_line(line) for line in dfdata.splitlines() if line]
+    result_mounts = []
 
-  @staticmethod
-  def _chk_mount(mountpoint):
-    try:
-      return call(['test', '-w', mountpoint], sudo=True, timeout=int(Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_DEFAULT)/2, quiet=(not logger.isEnabledFor(logging.DEBUG)))[0] == 0
-    except ExecuteTimeoutException:
-      logger.exception("Exception happened while checking mount {0}".format(mountpoint))
-      return False
+    for mount in mounts:
+      if not mount:
+        continue
+
+      """
+      We need to filter mounts by several parameters:
+       - mounted device is not in the ignored list
+       - is accessible to user under which current process running
+       - it is not file-mount (docker environment)
+      """
+      if mount["device"] not in cls.IGNORE_DEVICES and \
+         mount["mountpoint"].split("/")[0] not in cls.IGNORE_ROOT_MOUNTS and\
+         cls._chk_writable_mount(mount['mountpoint']) and \
+         not path_isfile(mount["mountpoint"]):
+
+        result_mounts.append(mount)
+
+    return result_mounts
+
+  @classmethod
+  def _chk_writable_mount(cls, mount_point):
+    if os.geteuid() == 0:
+      return os.access(mount_point, os.W_OK)
+    else:
+      try:
+        # test if mount point is writable for current user
+        call_result = call(['test', '-w', mount_point],
+                           sudo=True,
+                           timeout=int(Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_DEFAULT) / 2,
+                           quiet=not logger.isEnabledFor(logging.DEBUG))
+        return call_result and call_result[0] == 0
+      except ExecuteTimeoutException:
+        logger.exception("Exception happened while checking mount {0}".format(mount_point))
+        return False
+      except Fail:
+        logger.exception("Exception happened while checking mount {0}".format(mount_point))
+        return False
     
-  @staticmethod
+  @classmethod
   @OsFamilyFuncImpl(OSConst.WINSRV_FAMILY)
-  def osdisks(config = None):
+  def osdisks(cls, config=None):
     mounts = []
     runner = shellRunner()
     command_result = runner.runPowershell(script_block=Hardware.WINDOWS_GET_DRIVES_CMD)
@@ -117,12 +151,12 @@ class Hardware:
       return mounts
     else:
       for drive in [line for line in command_result.output.split(os.linesep) if line != '']:
-        available, used, percent, size, type, mountpoint = drive.split(" ")
+        available, used, percent, size, fs_type, mountpoint = drive.split(" ")
         mounts.append({"available": available,
                        "used": used,
                        "percent": percent,
                        "size": size,
-                       "type": type,
+                       "type": fs_type,
                        "mountpoint": mountpoint})
 
     return mounts
@@ -130,9 +164,12 @@ class Hardware:
   def get(self):
     return self.hardware
 
-def main(argv=None):
-  hardware = Hardware()
-  print hardware.get()
+
+def main():
+  from resource_management.core.logger import Logger
+  Logger.initialize_logger()
+
+  print Hardware().get()
 
 if __name__ == '__main__':
   main()

http://git-wip-us.apache.org/repos/asf/ambari/blob/f5f1272b/ambari-agent/src/test/python/ambari_agent/TestController.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/ambari_agent/TestController.py b/ambari-agent/src/test/python/ambari_agent/TestController.py
index 59b6276..59b41cd 100644
--- a/ambari-agent/src/test/python/ambari_agent/TestController.py
+++ b/ambari-agent/src/test/python/ambari_agent/TestController.py
@@ -186,7 +186,7 @@ class TestController(unittest.TestCase):
 
 
   @patch("subprocess.Popen")
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
+  @patch.object(Hardware, "_chk_writable_mount", new = MagicMock(return_value=True))
   @patch.object(FacterLinux, "facterInfo", new = MagicMock(return_value={}))
   @patch.object(FacterLinux, "__init__", new = MagicMock(return_value = None))
   @patch("urllib2.build_opener")
@@ -228,7 +228,7 @@ class TestController(unittest.TestCase):
 
 
   @patch("subprocess.Popen")
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
+  @patch.object(Hardware, "_chk_writable_mount", new = MagicMock(return_value=True))
   @patch.object(FacterLinux, "facterInfo", new = MagicMock(return_value={}))
   @patch.object(FacterLinux, "__init__", new = MagicMock(return_value = None))
   @patch("urllib2.build_opener")

http://git-wip-us.apache.org/repos/asf/ambari/blob/f5f1272b/ambari-agent/src/test/python/ambari_agent/TestHardware.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/ambari_agent/TestHardware.py b/ambari-agent/src/test/python/ambari_agent/TestHardware.py
index 661dac6..5c014db 100644
--- a/ambari-agent/src/test/python/ambari_agent/TestHardware.py
+++ b/ambari-agent/src/test/python/ambari_agent/TestHardware.py
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 
-'''
+"""
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
@@ -16,11 +16,12 @@ distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-'''
+"""
+
 from ambari_agent import main
 main.MEMORY_LEAK_DEBUG_FILEPATH = "/tmp/memory_leak_debug.out"
 from unittest import TestCase
-from mock.mock import patch, MagicMock
+from mock.mock import patch, MagicMock, Mock
 import unittest
 import platform
 import socket
@@ -30,22 +31,22 @@ from ambari_agent.Hardware import Hardware
 from ambari_agent.AmbariConfig import AmbariConfig
 from ambari_agent.Facter import Facter, FacterLinux
 from ambari_commons import OSCheck
-from glob import glob
+
 
 @not_for_platform(PLATFORM_WINDOWS)
-@patch.object(platform,"linux_distribution", new = ('Suse','11','Final'))
-@patch.object(socket, "getfqdn", new = MagicMock(return_value = "ambari.apache.org"))
-@patch.object(socket, "gethostbyname", new = MagicMock(return_value = "192.168.1.1"))
-@patch.object(FacterLinux, "setDataIfConfigShortOutput", new = MagicMock(return_value ='''Iface   MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
+@patch.object(platform, "linux_distribution", new=MagicMock(return_value=('Suse', '11', 'Final')))
+@patch.object(socket, "getfqdn", new=MagicMock(return_value="ambari.apache.org"))
+@patch.object(socket, "gethostbyname", new=MagicMock(return_value="192.168.1.1"))
+@patch.object(FacterLinux, "setDataIfConfigShortOutput", new=MagicMock(return_value='''Iface   MTU Met    RX-OK RX-ERR RX-DRP RX-OVR    TX-OK TX-ERR TX-DRP TX-OVR Flg
 eth0   1500   0     9986      0      0      0     5490      0      0      0 BMRU
 eth1   1500   0        0      0      0      0        6      0      0      0 BMRU
 eth2   1500   0        0      0      0      0        6      0      0      0 BMRU
 lo    16436   0        2      0      0      0        2      0      0      0 LRU'''))
 class TestHardware(TestCase):
  
-  @patch.object(Hardware, "osdisks", new = MagicMock(return_value=[]))
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
-  @patch.object(FacterLinux, "get_ip_address_by_ifname", new = MagicMock(return_value=None))
+  @patch.object(Hardware, "osdisks", new=MagicMock(return_value=[]))
+  @patch.object(Hardware, "_chk_writable_mount", new=MagicMock(return_value=True))
+  @patch.object(FacterLinux, "get_ip_address_by_ifname", new=MagicMock(return_value=None))
   @patch.object(OSCheck, "get_os_type")
   @patch.object(OSCheck, "get_os_version")
   def test_build(self, get_os_version_mock, get_os_type_mock):
@@ -57,23 +58,68 @@ class TestHardware(TestCase):
     for dev_item in result['mounts']:
       self.assertTrue(dev_item['available'] >= 0)
       self.assertTrue(dev_item['used'] >= 0)
-      self.assertTrue(dev_item['percent'] != None)
-      self.assertTrue(dev_item['device'] != None)
-      self.assertTrue(dev_item['mountpoint'] != None)
-      self.assertTrue(dev_item['type'] != None)
+      self.assertTrue(dev_item['percent'] is not None)
+      self.assertTrue(dev_item['device'] is not None)
+      self.assertTrue(dev_item['mountpoint'] is not None)
+      self.assertTrue(dev_item['type'] is not None)
       self.assertTrue(dev_item['size'] > 0)
 
     for os_disk_item in osdisks:
       self.assertTrue(os_disk_item['available'] >= 0)
       self.assertTrue(os_disk_item['used'] >= 0)
-      self.assertTrue(os_disk_item['percent'] != None)
-      self.assertTrue(os_disk_item['device'] != None)
-      self.assertTrue(os_disk_item['mountpoint'] != None)
-      self.assertTrue(os_disk_item['type'] != None)
+      self.assertTrue(os_disk_item['percent'] is not None)
+      self.assertTrue(os_disk_item['device'] is not None)
+      self.assertTrue(os_disk_item['mountpoint'] is not None)
+      self.assertTrue(os_disk_item['type'] is not None)
       self.assertTrue(os_disk_item['size'] > 0)
 
     self.assertTrue(len(result['mounts']) == len(osdisks))
 
+  @patch.object(Hardware, "_chk_writable_mount")
+  @patch("ambari_agent.Hardware.path_isfile")
+  def test_osdisks_parsing(self, isfile_mock, chk_writable_mount_mock):
+    df_output =\
+                """Filesystem                                                                                        Type  1024-blocks     Used Available Capacity Mounted on
+                /dev/mapper/docker-253:0-4980899-d45c264d37ab18c8ed14f890f4d59ac2b81e1c52919eb36a79419787209515f3 xfs      31447040  1282384  30164656       5% /
+                tmpfs                                                                                             tmpfs    32938336        4  32938332       1% /dev
+                tmpfs                                                                                             tmpfs    32938336        0  32938336       0% /sys/fs/cgroup
+                /dev/mapper/fedora-root                                                                           ext4    224161316 12849696 199901804       7% /etc/resolv.conf
+                /dev/mapper/fedora-root                                                                           ext4    224161316 12849696 199901804       7% /etc/hostname
+                /dev/mapper/fedora-root                                                                           ext4    224161316 12849696 199901804       7% /etc/hosts
+                shm                                                                                               tmpfs       65536        0     65536       0% /dev/shm
+                /dev/mapper/fedora-root                                                                           ext4    224161316 12849696 199901804       7% /run/secrets
+                """
+
+    def isfile_side_effect(path):
+      assume_files = ["/etc/resolv.conf", "/etc/hostname", "/etc/hosts"]
+      return path in assume_files
+
+    def chk_writable_mount_side_effect(path):
+      assume_read_only = ["/run/secrets"]
+      return path not in assume_read_only
+
+    isfile_mock.side_effect = isfile_side_effect
+    chk_writable_mount_mock.side_effect = chk_writable_mount_side_effect
+
+    with patch("subprocess.Popen") as open_mock:
+      proc_mock = Mock()
+      attr = {
+        'communicate.return_value': [
+          df_output
+        ]
+      }
+      proc_mock.configure_mock(**attr)
+      open_mock.return_value = proc_mock
+
+      result = Hardware.osdisks()
+
+    self.assertEquals(1, len(result))
+
+    expected_mounts_left = ["/"]
+    mounts_left = [item["mountpoint"] for item in result]
+
+    self.assertEquals(expected_mounts_left, mounts_left)
+
   @patch.object(OSCheck, "get_os_type")
   @patch.object(OSCheck, "get_os_version")
   @patch("subprocess.Popen")
@@ -83,57 +129,65 @@ class TestHardware(TestCase):
     get_os_type_mock.return_value = "suse"
     get_os_version_mock.return_value = "11"
     Hardware.osdisks()
-    popen_mock.assert_called_with(['timeout', '10', "df","-kPT"], stdout=-1)
+    popen_mock.assert_called_with(['timeout', '10', "df", "-kPT"], stdout=-1)
+
     config = AmbariConfig()
     Hardware.osdisks(config)
-    popen_mock.assert_called_with(['timeout', '10', "df","-kPT"], stdout=-1)
+    popen_mock.assert_called_with(['timeout', '10', "df", "-kPT"], stdout=-1)
+
     config.add_section(AmbariConfig.AMBARI_PROPERTIES_CATEGORY)
     config.set(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_KEY, "true")
     Hardware.osdisks(config)
-    popen_mock.assert_called_with(['timeout', '10', "df","-kPT"], stdout=-1)
+    popen_mock.assert_called_with(['timeout', '10', "df", "-kPT"], stdout=-1)
+
     config.set(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_KEY, "false")
     Hardware.osdisks(config)
-    popen_mock.assert_called_with(['timeout', '10', "df","-kPT", "-l"], stdout=-1)
+    popen_mock.assert_called_with(['timeout', '10', "df", "-kPT", "-l"], stdout=-1)
+
     config.set(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY, "0")
     Hardware.osdisks(config)
-    popen_mock.assert_called_with(['timeout', '10', "df","-kPT","-l"], stdout=-1)
+    popen_mock.assert_called_with(['timeout', '10', "df", "-kPT", "-l"], stdout=-1)
+
     config.set(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY, "1")
     Hardware.osdisks(config)
-    popen_mock.assert_called_with(["timeout","1","df","-kPT","-l"], stdout=-1)
+    popen_mock.assert_called_with(["timeout", "1", "df", "-kPT", "-l"], stdout=-1)
+
     config.set(AmbariConfig.AMBARI_PROPERTIES_CATEGORY, Hardware.CHECK_REMOTE_MOUNTS_TIMEOUT_KEY, "2")
     Hardware.osdisks(config)
-    popen_mock.assert_called_with(["timeout","2","df","-kPT","-l"], stdout=-1)
-
-
-  def test_extractMountInfo(self):
-    outputLine = "device type size used available percent mountpoint"
-    result = Hardware.extractMountInfo(outputLine)
-
-    self.assertEquals(result['device'], 'device')
-    self.assertEquals(result['type'], 'type')
-    self.assertEquals(result['size'], 'size')
-    self.assertEquals(result['used'], 'used')
-    self.assertEquals(result['available'], 'available')
-    self.assertEquals(result['percent'], 'percent')
-    self.assertEquals(result['mountpoint'], 'mountpoint')
-
-    outputLine = ""
-    result = Hardware.extractMountInfo(outputLine)
-
-    self.assertEquals(result, None)
-
-    outputLine = "device type size used available percent"
-    result = Hardware.extractMountInfo(outputLine)
-
-    self.assertEquals(result, None)
-
-    outputLine = "device type size used available percent mountpoint info"
-    result = Hardware.extractMountInfo(outputLine)
-
-    self.assertEquals(result, None)
-
-  @patch.object(FacterLinux, "get_ip_address_by_ifname", new = MagicMock(return_value=None))
-  @patch.object(hostname,"hostname")
+    popen_mock.assert_called_with(["timeout", "2", "df", "-kPT", "-l"], stdout=-1)
+
+  def test_parse_df_line(self):
+    df_line_sample = "device type size used available percent mountpoint"
+
+    samples = [
+      {
+        "sample": df_line_sample,
+        "expected": dict(zip(df_line_sample.split(), df_line_sample.split()))
+      },
+      {
+        "sample": "device type size used available percent",
+        "expected": None,
+      },
+      {
+        "sample": "device type size used available percent mountpoint info",
+        "expected": None,
+      },
+      {
+        "sample": "",
+        "expected": None
+      }
+    ]
+
+    for sample in samples:
+      result = Hardware._parse_df_line(sample["sample"])
+      self.assertEquals(result, sample["expected"], "Failed with sample: '{0}', expected: {1}, got: {2}".format(
+        sample["sample"],
+        sample["expected"],
+        result
+      ))
+
+  @patch.object(FacterLinux, "get_ip_address_by_ifname", new=MagicMock(return_value=None))
+  @patch.object(hostname, "hostname")
   @patch.object(FacterLinux, "getFqdn")
   @patch.object(OSCheck, "get_os_type")
   @patch.object(OSCheck, "get_os_version")
@@ -148,7 +202,7 @@ class TestHardware(TestCase):
     self.assertEquals(result['domain'], "apache.org")
     self.assertEquals(result['fqdn'], (result['hostname'] + '.' + result['domain']))
 
-  @patch.object(FacterLinux, "get_ip_address_by_ifname", new = MagicMock(return_value=None))
+  @patch.object(FacterLinux, "get_ip_address_by_ifname", new=MagicMock(return_value=None))
   @patch.object(FacterLinux, "setDataUpTimeOutput")
   @patch.object(OSCheck, "get_os_type")
   @patch.object(OSCheck, "get_os_version")
@@ -163,7 +217,7 @@ class TestHardware(TestCase):
     self.assertEquals(result['uptime_hours'], '73')
     self.assertEquals(result['uptime_days'], '3')
 
-  @patch.object(FacterLinux, "get_ip_address_by_ifname", new = MagicMock(return_value=None))
+  @patch.object(FacterLinux, "get_ip_address_by_ifname", new=MagicMock(return_value=None))
   @patch.object(FacterLinux, "setMemInfoOutput")
   @patch.object(OSCheck, "get_os_type")
   @patch.object(OSCheck, "get_os_version")
@@ -238,8 +292,7 @@ SwapFree:        1598676 kB
     self.assertTrue(get_ip_address_by_ifname_mock.called)
     self.assertEquals(result['netmask'], None)
 
-
-  @patch.object(FacterLinux, "get_ip_address_by_ifname", new = MagicMock(return_value=None))
+  @patch.object(FacterLinux, "get_ip_address_by_ifname", new=MagicMock(return_value=None))
   @patch.object(OSCheck, "get_os_type")
   @patch.object(OSCheck, "get_os_family")
   @patch.object(OSCheck, "get_os_version")
@@ -267,7 +320,6 @@ SwapFree:        1598676 kB
     self.assertEquals(result['operatingsystem'], 'some_type_of_os')
     self.assertEquals(result['osfamily'], 'My_new_family')
 
-
   @patch("os.path.exists")
   @patch("os.path.isdir")
   @patch("json.loads")
@@ -316,7 +368,6 @@ SwapFree:        1598676 kB
     self.assertEquals('value', result['key'])
 
 
-
 if __name__ == "__main__":
   unittest.main()
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/f5f1272b/ambari-agent/src/test/python/ambari_agent/TestHeartbeat.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/ambari_agent/TestHeartbeat.py b/ambari-agent/src/test/python/ambari_agent/TestHeartbeat.py
index 99ccb4c..19fad56 100644
--- a/ambari-agent/src/test/python/ambari_agent/TestHeartbeat.py
+++ b/ambari-agent/src/test/python/ambari_agent/TestHeartbeat.py
@@ -76,7 +76,7 @@ class TestHeartbeat(TestCase):
     self.assertEquals(not heartbeat.reports, True, "Heartbeat should not contain task in progress")
 
   @patch("subprocess.Popen")
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
+  @patch.object(Hardware, "_chk_writable_mount", new = MagicMock(return_value=True))
   @patch.object(ActionQueue, "result")
   @patch.object(HostInfoLinux, "register")
   def test_no_mapping(self, register_mock, result_mock, Popen_mock):
@@ -202,7 +202,7 @@ class TestHeartbeat(TestCase):
     self.assertEquals(hb, expected)
 
   @patch("subprocess.Popen")
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
+  @patch.object(Hardware, "_chk_writable_mount", new = MagicMock(return_value=True))
   @patch.object(HostInfoLinux, 'register')
   def test_heartbeat_no_host_check_cmd_in_queue(self, register_mock, Popen_mock):
     config = AmbariConfig.AmbariConfig()
@@ -231,7 +231,7 @@ class TestHeartbeat(TestCase):
 
 
   @patch("subprocess.Popen")
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
+  @patch.object(Hardware, "_chk_writable_mount", new = MagicMock(return_value=True))
   @patch.object(HostInfoLinux, 'register')
   def test_heartbeat_host_check_no_cmd(self, register_mock, Popen_mock):
     config = AmbariConfig.AmbariConfig()

http://git-wip-us.apache.org/repos/asf/ambari/blob/f5f1272b/ambari-agent/src/test/python/ambari_agent/TestRegistration.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/ambari_agent/TestRegistration.py b/ambari-agent/src/test/python/ambari_agent/TestRegistration.py
index 0a70df2..f5e0288 100644
--- a/ambari-agent/src/test/python/ambari_agent/TestRegistration.py
+++ b/ambari-agent/src/test/python/ambari_agent/TestRegistration.py
@@ -34,7 +34,7 @@ from ambari_agent.Facter import FacterLinux
 class TestRegistration(TestCase):
 
   @patch("subprocess.Popen")
-  @patch.object(Hardware, "_chk_mount", new = MagicMock(return_value=True))
+  @patch.object(Hardware, "_chk_writable_mount", new = MagicMock(return_value=True))
   @patch.object(FacterLinux, "facterInfo", new = MagicMock(return_value={}))
   @patch.object(FacterLinux, "__init__", new = MagicMock(return_value = None))
   @patch("ambari_commons.firewall.run_os_command")