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 2015/05/27 13:48:31 UTC
ambari git commit: AMBARI-11419. Checks for package existence are too
slow. (aonishuk)
Repository: ambari
Updated Branches:
refs/heads/trunk a8fc9a263 -> b570fc1f7
AMBARI-11419. Checks for package existence are too slow. (aonishuk)
Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/b570fc1f
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/b570fc1f
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/b570fc1f
Branch: refs/heads/trunk
Commit: b570fc1f73785106f3dff59dd8c02b1c42613b88
Parents: a8fc9a2
Author: Andrew Onishuk <ao...@hortonworks.com>
Authored: Wed May 27 14:48:24 2015 +0300
Committer: Andrew Onishuk <ao...@hortonworks.com>
Committed: Wed May 27 14:48:24 2015 +0300
----------------------------------------------------------------------
.../resource_management/TestPackageResource.py | 92 +++++++++++---------
.../core/providers/package/apt.py | 46 +++++-----
.../core/providers/package/yumrpm.py | 48 +++++++---
.../core/providers/package/zypper.py | 46 +++++++---
.../python/resource_management/core/shell.py | 2 +-
.../python/resource_management/core/utils.py | 10 +++
6 files changed, 152 insertions(+), 92 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/ambari/blob/b570fc1f/ambari-agent/src/test/python/resource_management/TestPackageResource.py
----------------------------------------------------------------------
diff --git a/ambari-agent/src/test/python/resource_management/TestPackageResource.py b/ambari-agent/src/test/python/resource_management/TestPackageResource.py
index e585826..d69f278 100644
--- a/ambari-agent/src/test/python/resource_management/TestPackageResource.py
+++ b/ambari-agent/src/test/python/resource_management/TestPackageResource.py
@@ -16,6 +16,7 @@ See the License for the specific language governing permissions and
limitations under the License.
'''
+import sys
from unittest import TestCase
from mock.mock import patch, MagicMock, call
@@ -35,9 +36,9 @@ class TestPackageResource(TestCase):
with Environment('/') as env:
Package("some_package",
)
- call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep '^some-package$'"),
- call(['/usr/bin/apt-get', '-q', '-o', 'Dpkg::Options::=--force-confdef', '--allow-unauthenticated', '--assume-yes', 'install', 'some-package'], logoutput=False, sudo=True, env={'DEBIAN_FRONTEND': 'noninteractive'}),
- call(['/usr/bin/apt-get', 'update', '-qq'], logoutput=False, sudo=True)])
+ call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep ^some-package$"),
+ call(['/usr/bin/apt-get', '-q', '-o', 'Dpkg::Options::=--force-confdef', '--allow-unauthenticated', '--assume-yes', 'install', 'some-package'], logoutput=False, sudo=True, env={'DEBIAN_FRONTEND': 'noninteractive'}),
+ call(['/usr/bin/apt-get', 'update', '-qq'], logoutput=False, sudo=True)])
shell_mock.assert_has_calls([call(['/usr/bin/apt-get', '-q', '-o', 'Dpkg::Options::=--force-confdef',
'--allow-unauthenticated', '--assume-yes', 'install', 'some-package'], logoutput=False, sudo=True)])
@@ -50,8 +51,9 @@ class TestPackageResource(TestCase):
with Environment('/') as env:
Package("some_package",
)
- call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep '^some-package$'"),
- call(['/usr/bin/apt-get', '-q', '-o', 'Dpkg::Options::=--force-confdef', '--allow-unauthenticated', '--assume-yes', 'install', 'some-package'], logoutput=False, sudo=True, env={'DEBIAN_FRONTEND': 'noninteractive'})])
+ call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep ^some-package$"),
+ call(['/usr/bin/apt-get', '-q', '-o', 'Dpkg::Options::=--force-confdef', '--allow-unauthenticated', '--assume-yes', 'install', 'some-package'], logoutput=False, sudo=True, env={'DEBIAN_FRONTEND': 'noninteractive'})])
+
self.assertEqual(shell_mock.call_count, 0, "shell.checked_call shouldn't be called")
@@ -59,19 +61,11 @@ class TestPackageResource(TestCase):
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'ubuntu')
def test_action_install_regex_ubuntu(self, shell_mock, call_mock):
- call_mock.side_effect = [(0, None),
- (0, "some-package1\nsome-package2"),
- (0, "Some text.\nStatus: install ok installed\nSome text"),
- (0, "Some text.\nStatus: not installed\nSome text"),
- (0, None)]
+ call_mock.side_effect = [(0, None)]
with Environment('/') as env:
Package("some_package.*",
)
- call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep '^some-package.*$'"),
- call("apt-cache --names-only search '^some-package.*$' | awk '{print $1}'"),
- call("dpkg --status 'some-package1'"),
- call("dpkg --status 'some-package2'"),
- call(['/usr/bin/apt-get', '-q', '-o', 'Dpkg::Options::=--force-confdef', '--allow-unauthenticated', '--assume-yes', 'install', 'some-package.*'], logoutput=False, sudo=True, env={'DEBIAN_FRONTEND': 'noninteractive'})])
+ call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep ^some-package.*$")])
self.assertEqual(shell_mock.call_count, 0, "shell.checked_call shouldn't be called")
@patch.object(shell, "call")
@@ -86,57 +80,56 @@ class TestPackageResource(TestCase):
with Environment('/') as env:
Package("some_package.*",
)
- call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep '^some-package.*$'"),
- call("apt-cache --names-only search '^some-package.*$' | awk '{print $1}'"),
- call("dpkg --status 'some-package1'"),
- call("dpkg --status 'some-package2'")])
- self.assertEqual(call_mock.call_count, 4, "Package should not be installed")
+ call_mock.assert_has_calls([call("dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep ^some-package.*$")])
+ self.assertEqual(call_mock.call_count, 1, "Package should not be installed")
self.assertEqual(shell_mock.call_count, 0, "shell.checked_call shouldn't be called")
- @patch.object(shell, "call")
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'redhat')
- def test_action_install_rhel(self, shell_mock, call_mock):
- call_mock.return_value= (1, None)
+ def test_action_install_rhel(self, shell_mock):
+ sys.modules['yum'] = MagicMock()
+ sys.modules['yum'].YumBase.return_value = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb.simplePkgList.return_value = [('some_packag',)]
with Environment('/') as env:
Package("some_package",
)
- call_mock.assert_called_with("installed_pkgs=`rpm -qa 'some_package'` ; [ ! -z \"$installed_pkgs\" ]")
+ self.assertTrue(sys.modules['yum'].YumBase.return_value.rpmdb.simplePkgList.called)
shell_mock.assert_called_with(['/usr/bin/yum', '-d', '0', '-e', '0', '-y', 'install', 'some_package'], logoutput=False, sudo=True)
- @patch.object(shell, "call")
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'redhat')
- def test_action_install_pattern_rhel(self, shell_mock, call_mock):
- call_mock.side_effect=[(0, None), (1, "Some text")]
+ def test_action_install_pattern_rhel(self, shell_mock):
+ sys.modules['yum'] = MagicMock()
+ sys.modules['yum'].YumBase.return_value = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb.simplePkgList.return_value = [('some_packag',)]
with Environment('/') as env:
Package("some_package*",
)
- call_mock.assert_has_calls([call("installed_pkgs=`rpm -qa 'some_package*'` ; [ ! -z \"$installed_pkgs\" ]"),
- call("! yum list available 'some_package*'")])
shell_mock.assert_called_with(['/usr/bin/yum', '-d', '0', '-e', '0', '-y', 'install', 'some_package*'], logoutput=False, sudo=True)
- @patch.object(shell, "call")
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'redhat')
- def test_action_install_pattern_installed_rhel(self, shell_mock, call_mock):
- call_mock.side_effect=[(0, None), (0, "Some text")]
+ def test_action_install_pattern_installed_rhel(self, shell_mock):
+ sys.modules['yum'] = MagicMock()
+ sys.modules['yum'].YumBase.return_value = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb.simplePkgList.return_value = [('some_package_1_2_3',)]
with Environment('/') as env:
Package("some_package*",
)
- call_mock.assert_has_calls([call("installed_pkgs=`rpm -qa 'some_package*'` ; [ ! -z \"$installed_pkgs\" ]"),
- call("! yum list available 'some_package*'")])
self.assertEqual(shell_mock.call_count, 0, "shell.checked_call shouldn't be called")
- @patch.object(shell, "call")
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'suse')
- def test_action_install_suse(self, shell_mock, call_mock):
- call_mock.return_value= (1, None)
+ def test_action_install_suse(self, shell_mock):
+ sys.modules['rpm'] = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value.dbMatch.return_value = [{'name':'some_packages'}]
with Environment('/') as env:
Package("some_package",
)
- call_mock.assert_called_with("installed_pkgs=`rpm -qa 'some_package'` ; [ ! -z \"$installed_pkgs\" ]")
shell_mock.assert_called_with(['/usr/bin/zypper', '--quiet', 'install', '--auto-agree-with-licenses', '--no-confirm', 'some_package'], logoutput=False, sudo=True)
@patch.object(shell, "call")
@@ -151,22 +144,25 @@ class TestPackageResource(TestCase):
call("zypper --non-interactive search --type package --uninstalled-only --match-exact 'some_package*'")])
shell_mock.assert_called_with(['/usr/bin/zypper', '--quiet', 'install', '--auto-agree-with-licenses', '--no-confirm', 'some_package*'], logoutput=False, sudo=True)
- @patch.object(shell, "call")
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'suse')
- def test_action_install_pattern_suse(self, shell_mock, call_mock):
- call_mock.side_effect=[(0, None), (0, "Loading repository data...\nReading installed packages...\nNo packages found.\n")]
+ def test_action_install_pattern_suse(self, shell_mock):
+ sys.modules['rpm'] = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value.dbMatch.return_value = [{'name':'some_packagetest'}]
with Environment('/') as env:
Package("some_package*",
)
- call_mock.assert_has_calls([call("installed_pkgs=`rpm -qa 'some_package*'` ; [ ! -z \"$installed_pkgs\" ]"),
- call("zypper --non-interactive search --type package --uninstalled-only --match-exact 'some_package*'")])
self.assertEqual(shell_mock.call_count, 0, "shell.checked_call shouldn't be called")
@patch.object(shell, "call", new = MagicMock(return_value=(0, None)))
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'redhat')
def test_action_install_existent_rhel(self, shell_mock):
+ sys.modules['yum'] = MagicMock()
+ sys.modules['yum'].YumBase.return_value = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb.simplePkgList.return_value = [('some_package',)]
with Environment('/') as env:
Package("some_package",
)
@@ -188,6 +184,9 @@ class TestPackageResource(TestCase):
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'suse')
def test_action_install_existent_suse(self, shell_mock):
+ sys.modules['rpm'] = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value.dbMatch.return_value = [{'name':'some_package'}]
with Environment('/') as env:
Package("some_package",
)
@@ -197,6 +196,10 @@ class TestPackageResource(TestCase):
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'redhat')
def test_action_remove_rhel(self, shell_mock):
+ sys.modules['yum'] = MagicMock()
+ sys.modules['yum'].YumBase.return_value = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb = MagicMock()
+ sys.modules['yum'].YumBase.return_value.rpmdb.simplePkgList.return_value = [('some_package',)]
with Environment('/') as env:
Package("some_package",
action = "remove"
@@ -207,6 +210,9 @@ class TestPackageResource(TestCase):
@patch.object(shell, "checked_call")
@patch.object(System, "os_family", new = 'suse')
def test_action_remove_suse(self, shell_mock):
+ sys.modules['rpm'] = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value = MagicMock()
+ sys.modules['rpm'].TransactionSet.return_value.dbMatch.return_value = [{'name':'some_package'}]
with Environment('/') as env:
Package("some_package",
action = "remove"
http://git-wip-us.apache.org/repos/asf/ambari/blob/b570fc1f/ambari-common/src/main/python/resource_management/core/providers/package/apt.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/resource_management/core/providers/package/apt.py b/ambari-common/src/main/python/resource_management/core/providers/package/apt.py
index 76db791..c4cae2a 100644
--- a/ambari-common/src/main/python/resource_management/core/providers/package/apt.py
+++ b/ambari-common/src/main/python/resource_management/core/providers/package/apt.py
@@ -22,6 +22,7 @@ Ambari Agent
import os
import tempfile
import shutil
+import re
from resource_management.core.providers.package import PackageProvider
from resource_management.core import shell
@@ -39,15 +40,10 @@ REMOVE_CMD = {
}
REPO_UPDATE_CMD = ['/usr/bin/apt-get', 'update','-qq']
-CHECK_EXISTENCE_CMD = "dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep '^%s$'"
-GET_PACKAGES_BY_PATTERN_CMD = "apt-cache --names-only search '^%s$' | awk '{print $1}'"
-GET_PACKAGE_STATUS_CMD = "dpkg --status '%s'"
-
-PACKAGE_INSTALLED_STATUS = 'Status: install ok installed'
-
-EMPTY_FILE = "/dev/null"
APT_SOURCES_LIST_DIR = "/etc/apt/sources.list.d"
+CHECK_CMD = "dpkg --get-selections | grep -v deinstall | awk '{print $1}' | grep ^%s$"
+
def replace_underscores(function_to_decorate):
def wrapper(*args):
self = args[0]
@@ -60,7 +56,7 @@ class AptProvider(PackageProvider):
@replace_underscores
def install_package(self, name, use_repos=[]):
- if not self._check_existence(name) or use_repos:
+ if use_repos or not self._check_existence(name):
cmd = INSTALL_CMD[self.get_logoutput()]
copied_sources_files = []
is_tmp_dir_created = False
@@ -117,16 +113,24 @@ class AptProvider(PackageProvider):
Logger.info("Skipping removal of non-existing package %s" % (name))
@replace_underscores
- def _check_existence(self, name):
- code, out = shell.call(CHECK_EXISTENCE_CMD % name)
- if bool(code):
- return False
- elif '*' in name or '.' in name: # Check if all packages matching regexp are installed
- code1, out1 = shell.call(GET_PACKAGES_BY_PATTERN_CMD % name)
- for package_name in out1.splitlines():
- code2, out2 = shell.call(GET_PACKAGE_STATUS_CMD % package_name)
- if PACKAGE_INSTALLED_STATUS not in out2.splitlines():
- return False
- return True
- else:
- return True
+ def _check_existence(self, name):
+ """
+ For regexp names:
+ If only part of packages were installed during early canceling.
+ Let's say:
+ 1. install hbase-2-3-.*
+ 2. Only hbase-2-3-1234 is installed, but is not hbase-2-3-1234-regionserver yet.
+ 3. We cancel the apt-get
+
+ In that case this is bug of packages we require.
+ And hbase-2-3-*-regionserver should be added to metainfo.xml.
+
+ Checking existence should never fail in such a case for hbase-2-3-.*, otherwise it
+ gonna break things like removing packages and some other things.
+
+ Note: this method SHOULD NOT use apt-get (apt.cache is using dpkg not apt). Because a lot of issues we have, when customer have
+ apt-get in inconsistant state (locked, used, having invalid repo). Once packages are installed
+ we should not rely on that.
+ """
+ code, out = shell.call(CHECK_CMD % name)
+ return not bool(code)
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/b570fc1f/ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py b/ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py
index bfe7fa4..2d39c4d 100644
--- a/ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py
+++ b/ambari-common/src/main/python/resource_management/core/providers/package/yumrpm.py
@@ -24,7 +24,9 @@ from resource_management.core.providers.package import PackageProvider
from resource_management.core import shell
from resource_management.core.shell import string_cmd_from_args_list
from resource_management.core.logger import Logger
+from resource_management.core.utils import suppress_stdout
import os
+import re
INSTALL_CMD = {
True: ['/usr/bin/yum', '-y', 'install'],
@@ -36,12 +38,9 @@ REMOVE_CMD = {
False: ['/usr/bin/yum', '-d', '0', '-e', '0', '-y', 'erase'],
}
-CHECK_CMD = "installed_pkgs=`rpm -qa '%s'` ; [ ! -z \"$installed_pkgs\" ]"
-CHECK_AVAILABLE_PACKAGES_CMD = "! yum list available '%s'"
-
class YumProvider(PackageProvider):
def install_package(self, name, use_repos=[]):
- if not self._check_existence(name) or use_repos:
+ if use_repos or not self._check_existence(name):
cmd = INSTALL_CMD[self.get_logoutput()]
if use_repos:
enable_repo_option = '--enablerepo=' + ",".join(use_repos)
@@ -64,13 +63,34 @@ class YumProvider(PackageProvider):
Logger.info("Skipping removal of non-existing package %s" % (name))
def _check_existence(self, name):
- if '.' in name: # To work with names like 'zookeeper_2_2_1_0_2072.noarch'
- name = os.path.splitext(name)[0]
- code, out = shell.call(CHECK_CMD % name)
- if bool(code):
- return False
- elif '*' in name or '?' in name: # Check if all packages matching pattern are installed
- code1, out1 = shell.call(CHECK_AVAILABLE_PACKAGES_CMD % name)
- return not bool(code1)
- else:
- return True
+ """
+ For regexp names:
+ If only part of packages were installed during early canceling.
+ Let's say:
+ 1. install hbase_2_3_*
+ 2. Only hbase_2_3_1234 is installed, but is not hbase_2_3_1234_regionserver yet.
+ 3. We cancel the yum
+
+ In that case this is bug of packages we require.
+ And hbase_2_3_*_regionserver should be added to metainfo.xml.
+
+ Checking existence should never fail in such a case for hbase_2_3_*, otherwise it
+ gonna break things like removing packages and some others.
+
+ Note: this method SHOULD NOT use yum directly (yum.rpmdb doesn't use it). Because a lot of issues we have, when customer have
+ yum in inconsistant state (locked, used, having invalid repo). Once packages are installed
+ we should not rely on that.
+ """
+ import yum # Python Yum API is much faster then other check methods. (even then "import rpm")
+ yb = yum.YumBase()
+ name_regex = re.escape(name).replace("\\?", ".").replace("\\*", ".*") + '$'
+ regex = re.compile(name_regex)
+
+ with suppress_stdout():
+ package_list = yb.rpmdb.simplePkgList()
+
+ for package in package_list:
+ if regex.match(package[0]):
+ return True
+
+ return False
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/ambari/blob/b570fc1f/ambari-common/src/main/python/resource_management/core/providers/package/zypper.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/resource_management/core/providers/package/zypper.py b/ambari-common/src/main/python/resource_management/core/providers/package/zypper.py
index 367d3cc..b7f33f4 100644
--- a/ambari-common/src/main/python/resource_management/core/providers/package/zypper.py
+++ b/ambari-common/src/main/python/resource_management/core/providers/package/zypper.py
@@ -24,6 +24,9 @@ from resource_management.core.providers.package import PackageProvider
from resource_management.core import shell
from resource_management.core.shell import string_cmd_from_args_list
from resource_management.core.logger import Logger
+from resource_management.core.utils import suppress_stdout
+
+import re
INSTALL_CMD = {
True: ['/usr/bin/zypper', 'install', '--auto-agree-with-licenses', '--no-confirm'],
@@ -33,10 +36,6 @@ REMOVE_CMD = {
True: ['/usr/bin/zypper', 'remove', '--no-confirm'],
False: ['/usr/bin/zypper', '--quiet', 'remove', '--no-confirm'],
}
-CHECK_CMD = "installed_pkgs=`rpm -qa '%s'` ; [ ! -z \"$installed_pkgs\" ]"
-GET_NOT_INSTALLED_CMD = "zypper --non-interactive search --type package --uninstalled-only --match-exact '%s'"
-
-NO_PACKAGES_FOUND_STATUS = 'No packages found.'
LIST_ACTIVE_REPOS_CMD = ['/usr/bin/zypper', 'repos']
@@ -55,7 +54,7 @@ def get_active_base_repos():
class ZypperProvider(PackageProvider):
def install_package(self, name, use_repos=[]):
- if not self._check_existence(name) or use_repos:
+ if use_repos or not self._check_existence(name):
cmd = INSTALL_CMD[self.get_logoutput()]
if use_repos:
active_base_repos = get_active_base_repos()
@@ -85,11 +84,32 @@ class ZypperProvider(PackageProvider):
Logger.info("Skipping removal of non-existing package %s" % (name))
def _check_existence(self, name):
- code, out = shell.call(CHECK_CMD % name)
- if bool(code):
- return False
- elif '*' in name or '?' in name: # Check if all packages matching pattern are installed
- code1, out1 = shell.call(GET_NOT_INSTALLED_CMD % name)
- return NO_PACKAGES_FOUND_STATUS in out1.splitlines()
- else:
- return True
+ """
+ For regexp names:
+ If only part of packages were installed during early canceling.
+ Let's say:
+ 1. install hbase_2_3_*
+ 2. Only hbase_2_3_1234 is installed, but is not hbase_2_3_1234_regionserver yet.
+ 3. We cancel the zypper
+
+ In that case this is bug of packages we require.
+ And hbase_2_3_*_regionserver should be added to metainfo.xml.
+
+ Checking existence should never fail in such a case for hbase_2_3_*, otherwise it
+ gonna break things like removing packages and some other things.
+
+ Note: this method SHOULD NOT use zypper. Because a lot of issues we have, when customer have
+ zypper in inconsistant state (locked, used, having invalid repo). Once packages are installed
+ we should not rely on that.
+ """
+ import rpm # this is faster then calling 'rpm'-binary externally.
+ ts = rpm.TransactionSet()
+ packages = ts.dbMatch()
+
+ name_regex = re.escape(name).replace("\\?", ".").replace("\\*", ".*") + '$'
+ regex = re.compile(name_regex)
+
+ for package in packages:
+ if regex.match(package['name']):
+ return True
+ return False
http://git-wip-us.apache.org/repos/asf/ambari/blob/b570fc1f/ambari-common/src/main/python/resource_management/core/shell.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/resource_management/core/shell.py b/ambari-common/src/main/python/resource_management/core/shell.py
index f2b5000..71b6501 100644
--- a/ambari-common/src/main/python/resource_management/core/shell.py
+++ b/ambari-common/src/main/python/resource_management/core/shell.py
@@ -170,7 +170,7 @@ def _call(command, logoutput=None, throw_on_failure=True,
if ready:
line = os.read(master_fd, 512)
if not line:
- break
+ continue
out += line
try:
http://git-wip-us.apache.org/repos/asf/ambari/blob/b570fc1f/ambari-common/src/main/python/resource_management/core/utils.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/resource_management/core/utils.py b/ambari-common/src/main/python/resource_management/core/utils.py
index 52a12b3..d9f678b 100644
--- a/ambari-common/src/main/python/resource_management/core/utils.py
+++ b/ambari-common/src/main/python/resource_management/core/utils.py
@@ -20,6 +20,9 @@ Ambari Agent
"""
+import contextlib
+import sys
+import cStringIO
from resource_management.core.exceptions import Fail
class AttributeDictionary(object):
@@ -106,3 +109,10 @@ def checked_unite(dict1, dict2):
result.update(dict2)
return result
+
+@contextlib.contextmanager
+def suppress_stdout():
+ save_stdout = sys.stdout
+ sys.stdout = cStringIO.StringIO()
+ yield
+ sys.stdout = save_stdout