Blame SOURCES/python-rhsm-1.15.4-2-to-python-rhsm-1.15.4-3.patch

84ba10
diff --git a/python-rhsm.spec b/python-rhsm.spec
84ba10
index 0318cc1..d2e00c1 100644
84ba10
--- a/python-rhsm.spec
84ba10
+++ b/python-rhsm.spec
84ba10
@@ -13,7 +13,7 @@
84ba10
 
84ba10
 Name: python-rhsm
84ba10
 Version: 1.15.4
84ba10
-Release: 2%{?dist}
84ba10
+Release: 3%{?dist}
84ba10
 
84ba10
 Summary: A Python library to communicate with a Red Hat Unified Entitlement Platform
84ba10
 Group: Development/Libraries
84ba10
@@ -74,6 +74,11 @@ rm -rf %{buildroot}
84ba10
 %attr(644,root,root) %{_sysconfdir}/rhsm/ca/*.pem
84ba10
 
84ba10
 %changelog
84ba10
+* Wed Aug 12 2015 Chris Rog <crog@redhat.com> 1.15.4-3
84ba10
+- Add user-agent to rhsm requests. (alikins@redhat.com)
84ba10
+- 1247890: KeyErrors are now caught when checking manager capabilities
84ba10
+  (csnyder@redhat.com)
84ba10
+
84ba10
 * Thu Jul 23 2015 Chris Rog <crog@redhat.com> 1.15.4-2
84ba10
 - Fedora 20 has been retired. (awood@redhat.com)
84ba10
 
84ba10
diff --git a/rel-eng/packages/python-rhsm b/rel-eng/packages/python-rhsm
84ba10
index ace4dde..c370a05 100644
84ba10
--- a/rel-eng/packages/python-rhsm
84ba10
+++ b/rel-eng/packages/python-rhsm
84ba10
@@ -1 +1 @@
84ba10
-1.15.4-2 ./
84ba10
+1.15.4-3 ./
84ba10
diff --git a/rel-eng/tito.props b/rel-eng/tito.props
84ba10
index fd057f6..6c86a16 100644
84ba10
--- a/rel-eng/tito.props
84ba10
+++ b/rel-eng/tito.props
84ba10
@@ -1,4 +1,4 @@
84ba10
-[buildconfig]
84ba10
-builder = tito.builder.Builder
84ba10
-tagger = tito.tagger.VersionTagger
84ba10
+[globalconfig]
84ba10
+default_builder = tito.distributionbuilder.DistributionBuilder
84ba10
+default_tagger = tito.tagger.ReleaseTagger
84ba10
 
84ba10
diff --git a/src/rhsm/connection.py b/src/rhsm/connection.py
84ba10
index 465405d..f5cbbdf 100644
84ba10
--- a/src/rhsm/connection.py
84ba10
+++ b/src/rhsm/connection.py
84ba10
@@ -43,7 +43,7 @@ except ImportError:
84ba10
     subman_version = "unknown"
84ba10
 
84ba10
 from rhsm import ourjson as json
84ba10
-from rhsm.utils import get_env_proxy_info
84ba10
+from rhsm import utils
84ba10
 
84ba10
 global_socket_timeout = 60
84ba10
 timeout_altered = None
84ba10
@@ -115,6 +115,7 @@ def drift_check(utc_time_string, hours=1):
84ba10
     return drift
84ba10
 
84ba10
 
84ba10
+
84ba10
 class ConnectionException(Exception):
84ba10
     pass
84ba10
 
84ba10
@@ -264,7 +265,6 @@ class ContentConnection(object):
84ba10
         self.username = username
84ba10
         self.password = password
84ba10
         self.ssl_verify_depth = ssl_verify_depth
84ba10
-
84ba10
         self.timeout_altered = False
84ba10
 
84ba10
         # get the proxy information from the environment variable
84ba10
@@ -275,13 +275,17 @@ class ContentConnection(object):
84ba10
                    'proxy_port': '',
84ba10
                    'proxy_password': ''}
84ba10
         else:
84ba10
-            info = get_env_proxy_info()
84ba10
+            info = utils.get_env_proxy_info()
84ba10
 
84ba10
         self.proxy_hostname = proxy_hostname or config.get('server', 'proxy_hostname') or info['proxy_hostname']
84ba10
         self.proxy_port = proxy_port or config.get('server', 'proxy_port') or info['proxy_port']
84ba10
         self.proxy_user = proxy_user or config.get('server', 'proxy_user') or info['proxy_username']
84ba10
         self.proxy_password = proxy_password or config.get('server', 'proxy_password') or info['proxy_password']
84ba10
 
84ba10
+    @property
84ba10
+    def user_agent(self):
84ba10
+        return "RHSM-content/1.0 (cmd=%s)" % utils.cmd_name(sys.argv)
84ba10
+
84ba10
     def _request(self, request_type, handler, body=None):
84ba10
         # See note in Restlib._request
84ba10
         context = SSL.Context("sslv23")
84ba10
@@ -304,7 +308,11 @@ class ContentConnection(object):
84ba10
 
84ba10
         set_default_socket_timeout_if_python_2_3()
84ba10
 
84ba10
-        conn.request("GET", handler, body="", headers={"Host": "%s:%s" % (self.host, self.ssl_port), "Content-Length": "0"})
84ba10
+        conn.request("GET", handler,
84ba10
+                     body="",
84ba10
+                     headers={"Host": "%s:%s" % (self.host, self.ssl_port),
84ba10
+                              "Content-Length": "0",
84ba10
+                              "User-Agent": self.user_agent})
84ba10
         response = conn.getresponse()
84ba10
         result = {
84ba10
             "content": response.read(),
84ba10
@@ -383,6 +391,9 @@ class Restlib(object):
84ba10
         self.apihandler = apihandler
84ba10
         lc = _get_locale()
84ba10
 
84ba10
+        # Default, updated by UepConnection
84ba10
+        self.user_agent = "python-rhsm-user-agent"
84ba10
+
84ba10
         self.headers = {"Content-type": "application/json",
84ba10
                         "Accept": "application/json",
84ba10
                         "x-python-rhsm-version": python_rhsm_version,
84ba10
@@ -492,6 +503,7 @@ class Restlib(object):
84ba10
         else:
84ba10
             conn = httpslib.HTTPSConnection(self.host, self.ssl_port, ssl_context=context)
84ba10
 
84ba10
+
84ba10
         if info is not None:
84ba10
             body = json.dumps(info, default=json.encode)
84ba10
         else:
84ba10
@@ -499,6 +511,9 @@ class Restlib(object):
84ba10
 
84ba10
         log.debug("Making request: %s %s" % (request_type, handler))
84ba10
 
84ba10
+        if self.user_agent:
84ba10
+            self.headers['User-Agent'] = self.user_agent
84ba10
+
84ba10
         headers = self.headers
84ba10
         if body is None:
84ba10
             headers = dict(self.headers.items() +
84ba10
@@ -665,7 +680,7 @@ class UEPConnection:
84ba10
                    'proxy_port': '',
84ba10
                    'proxy_password': ''}
84ba10
         else:
84ba10
-            info = get_env_proxy_info()
84ba10
+            info = utils.get_env_proxy_info()
84ba10
 
84ba10
         self.proxy_hostname = proxy_hostname or config.get('server', 'proxy_hostname') or info['proxy_hostname']
84ba10
         self.proxy_port = proxy_port or config.get('server', 'proxy_port') or info['proxy_port']
84ba10
@@ -731,6 +746,8 @@ class UEPConnection:
84ba10
                     ssl_verify_depth=self.ssl_verify_depth)
84ba10
             auth_description = "auth=none"
84ba10
 
84ba10
+        self.conn.user_agent = "RHSM/1.0 (cmd=%s)" % utils.cmd_name(sys.argv)
84ba10
+
84ba10
         self.resources = None
84ba10
         self.capabilities = None
84ba10
         connection_description = ""
84ba10
@@ -771,21 +788,27 @@ class UEPConnection:
84ba10
     def _load_manager_capabilities(self):
84ba10
         """
84ba10
         Loads manager capabilities by doing a GET on the status
84ba10
-        resource located at '/status/'
84ba10
-        """
84ba10
-        self.capabilities = {}
84ba10
-        self.capabilities = self.conn.request_get("/status/")
84ba10
-        self.capabilities = self.capabilities['managerCapabilities']
84ba10
-        log.debug("Server has the following capabilities: %s",
84ba10
-                  self.capabilities)
84ba10
+        resource located at '/status'
84ba10
+        """
84ba10
+        status = self.getStatus()
84ba10
+        capabilities = status.get('managerCapabilities')
84ba10
+        if capabilities is None:
84ba10
+            log.debug("The status retrieved did not \
84ba10
+                      include key 'managerCapabilities'.\nStatus:'%s'" % status)
84ba10
+            capabilities = []
84ba10
+        elif isinstance(capabilities, list) and not capabilities:
84ba10
+            log.debug("The managerCapabilities list \
84ba10
+                      was empty\nStatus:'%s'" % status)
84ba10
+        else:
84ba10
+            log.debug("Server has the following capabilities: %s", capabilities)
84ba10
+        return capabilities
84ba10
 
84ba10
     def has_capability(self, capability):
84ba10
         """
84ba10
         Check if the server we're connected to has a particular capability.
84ba10
         """
84ba10
         if self.capabilities is None:
84ba10
-            self._load_manager_capabilities()
84ba10
-
84ba10
+            self.capabilities = self._load_manager_capabilities()
84ba10
         return capability in self.capabilities
84ba10
 
84ba10
     def shutDown(self):
84ba10
diff --git a/src/rhsm/utils.py b/src/rhsm/utils.py
84ba10
index dd41161..f1a7f4e 100644
84ba10
--- a/src/rhsm/utils.py
84ba10
+++ b/src/rhsm/utils.py
84ba10
@@ -17,6 +17,7 @@ import gettext
84ba10
 import os
84ba10
 import re
84ba10
 from urlparse import urlparse
84ba10
+
84ba10
 from rhsm.config import DEFAULT_PROXY_PORT
84ba10
 
84ba10
 _ = lambda x: gettext.ldgettext("rhsm", x)
84ba10
@@ -232,3 +233,24 @@ def get_env_proxy_info():
84ba10
         else:
84ba10
             the_proxy['proxy_port'] = int(info[3])
84ba10
     return the_proxy
84ba10
+
84ba10
+
84ba10
+def cmd_name(argv):
84ba10
+    """Attempt to get a meaningful command name from argv.
84ba10
+
84ba10
+    This handles cases where argv[0] isn't helpful (for
84ba10
+    example, '/usr/bin/python' or '__main__.py'.
84ba10
+    """
84ba10
+    argv0 = os.path.basename(argv[0])
84ba10
+    argvdir = os.path.dirname(argv[0])
84ba10
+    head, tail = os.path.split(argvdir)
84ba10
+
84ba10
+    cmd_name_string = argv0
84ba10
+    # initial-setup is launched as 'python -m initial_setup', so
84ba10
+    # sys.argv looks like
84ba10
+    # ['/usr/lib/python2.7/site-packages/initial_setup/__main__.py'],
84ba10
+    # so we look for initial_setup in the exe path.
84ba10
+    if tail == "initial_setup":
84ba10
+        cmd_name_string = "initial-setup"
84ba10
+
84ba10
+    return cmd_name_string
84ba10
diff --git a/test/unit/connection-tests.py b/test/unit/connection-tests.py
84ba10
index 6e449ad..8a0091d 100644
84ba10
--- a/test/unit/connection-tests.py
84ba10
+++ b/test/unit/connection-tests.py
84ba10
@@ -37,6 +37,26 @@ class ConnectionTests(unittest.TestCase):
84ba10
         self.cp = UEPConnection(username="dummy", password="dummy",
84ba10
                 handler="/Test/", insecure=True)
84ba10
 
84ba10
+    def test_load_manager_capabilities(self):
84ba10
+        expected_capabilities = ['hypervisors_async', 'cores']
84ba10
+        proper_status = {'version':'1',
84ba10
+                         'result':True,
84ba10
+                         'managerCapabilities':expected_capabilities}
84ba10
+        improper_status = dict.copy(proper_status)
84ba10
+        # Remove the managerCapabilities key from the dict
84ba10
+        del improper_status['managerCapabilities']
84ba10
+        self.cp.conn = Mock()
84ba10
+        # The first call will return the proper_status, the second, the improper
84ba10
+        # status
84ba10
+        original_getStatus = self.cp.getStatus
84ba10
+        self.cp.getStatus = Mock(side_effect=[proper_status,
84ba10
+                                                     improper_status])
84ba10
+        actual_capabilities = self.cp._load_manager_capabilities()
84ba10
+        self.assertEquals(sorted(actual_capabilities),
84ba10
+                          sorted(expected_capabilities))
84ba10
+        self.assertEquals([], self.cp._load_manager_capabilities())
84ba10
+        self.cp.getStatus = original_getStatus
84ba10
+
84ba10
     def test_get_environment_by_name_requires_owner(self):
84ba10
         self.assertRaises(Exception, self.cp.getEnvironment, None, {"name": "env name"})
84ba10
 
84ba10
diff --git a/test/unit/util-tests.py b/test/unit/util-tests.py
84ba10
index c650d74..4d4a65b 100644
84ba10
--- a/test/unit/util-tests.py
84ba10
+++ b/test/unit/util-tests.py
84ba10
@@ -6,7 +6,7 @@ from rhsm.utils import remove_scheme, get_env_proxy_info, \
84ba10
     ServerUrlParseErrorEmpty, ServerUrlParseErrorNone, \
84ba10
     ServerUrlParseErrorPort, ServerUrlParseErrorScheme, \
84ba10
     ServerUrlParseErrorJustScheme, has_bad_scheme, has_good_scheme, \
84ba10
-    parse_url
84ba10
+    parse_url, cmd_name
84ba10
 from rhsm.config import DEFAULT_PORT, DEFAULT_PREFIX, DEFAULT_HOSTNAME, \
84ba10
     DEFAULT_CDN_HOSTNAME, DEFAULT_CDN_PORT, DEFAULT_CDN_PREFIX
84ba10
 
84ba10
@@ -379,3 +379,45 @@ class TestProxyInfo(unittest.TestCase):
84ba10
             self.assertEquals(None, proxy_info["proxy_password"])
84ba10
             self.assertEquals("host", proxy_info["proxy_hostname"])
84ba10
             self.assertEquals(int("1111"), proxy_info["proxy_port"])
84ba10
+
84ba10
+
84ba10
+class TestCmdName(unittest.TestCase):
84ba10
+    def test_usr_sbin(self):
84ba10
+        argv = ['/usr/sbin/subscription-manager', 'list']
84ba10
+        self.assertEquals("subscription-manager", cmd_name(argv))
84ba10
+
84ba10
+    def test_bin(self):
84ba10
+        argv = ['bin/subscription-manager', 'subscribe', '--auto']
84ba10
+        self.assertEquals("subscription-manager", cmd_name(argv))
84ba10
+
84ba10
+    def test_sbin(self):
84ba10
+        argv = ['/sbin/subscription-manager', 'list']
84ba10
+        self.assertEquals("subscription-manager", cmd_name(argv))
84ba10
+
84ba10
+    def test_subscription_manager_gui(self):
84ba10
+        argv = ['/sbin/subscription-manager-gui']
84ba10
+        self.assertEquals("subscription-manager-gui", cmd_name(argv))
84ba10
+
84ba10
+    def test_initial_setup(self):
84ba10
+        argv = ['/usr/lib/python2.7/site-packages/initial_setup/__main__.py']
84ba10
+        self.assertEquals("initial-setup", cmd_name(argv))
84ba10
+
84ba10
+    def test_yum(self):
84ba10
+        argv = ['/bin/yum', 'install', 'zsh']
84ba10
+        self.assertEquals("yum", cmd_name(argv))
84ba10
+
84ba10
+    def test_rhsmcertd_worker(self):
84ba10
+        argv = ['/usr/libexec/rhsmcertd-worker']
84ba10
+        self.assertEquals("rhsmcertd-worker", cmd_name(argv))
84ba10
+
84ba10
+    def test_rhsm_debug(self):
84ba10
+        argv = ['/bin/rhsm-debug']
84ba10
+        self.assertEquals("rhsm-debug", cmd_name(argv))
84ba10
+
84ba10
+    def test_virt_who(self):
84ba10
+        argv = ['/usr/share/virt-who/virtwho.py']
84ba10
+        self.assertEquals("virtwho.py", cmd_name(argv))
84ba10
+
84ba10
+    def test_rhsmd(self):
84ba10
+        argv = ['/usr/libexec/rhsmd', '-i', '-f', 'valid']
84ba10
+        self.assertEquals("rhsmd", cmd_name(argv))