~free.ekanayaka/landscape-client/lucid-1.5.2.1-0ubuntu0.10.04.0

« back to all changes in this revision

Viewing changes to landscape/package/reporter.py

  • Committer: Bazaar Package Importer
  • Author(s): Mathias Gug, Free Ekanayaka
  • Date: 2009-07-22 14:54:50 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090722145450-pvbp13gh8734c8ft
Tags: 1.3.2.2-0ubuntu0.9.10.1
[ Free Ekanayaka ]
* New upstream release:
  - Include the README file in landscape-client (LP: #396260)
  - Fix client capturing stderr from run_command when constructing
    hash-id-databases url (LP: #397480)
  - Use substvars to conditionally depend on update-motd or
    libpam-modules (LP: #393454)
  - Fix reporting wrong version to the server (LP: #391225)
  - The init script does not wait for the network to be available
    before checking for EC2 user data (LP: #383336)
  - When the broker is restarted by the watchdog, the state of the client
    is inconsistent (LP: #380633)
  - Package stays unknown forever in the client with hash-id-databases
    support (LP: #381356)
  - Standard error not captured when calling smart-update (LP: #387441)
  - Changer calls reporter without switching groups, just user (LP: #388092)
  - Run smart update in the package-reporter instead of having a cronjob (LP: #362355)
  - Package changer does not inherit proxy settings (LP: #381241)
  - The ./test script doesn't work in landscape-client (LP: #381613)
  - The source package should build on all supported releases (LP: #385098)
  - Strip smart update's output (LP: #387331)
  - The fetch() timeout isn't based on activity (#389224)
  - Client can use a UUID of "None" when fetching the hash-id-database (LP: #381291)
  - Registration should use the fqdn rather than just the hostname (LP: #385730)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import urlparse
1
2
import logging
2
3
import time
3
4
import sys
4
5
import os
5
6
 
6
7
from twisted.internet.defer import Deferred, succeed
 
8
from twisted.internet.utils import getProcessOutputAndValue
7
9
 
8
10
from landscape.lib.sequenceranges import sequence_to_ranges
9
11
from landscape.lib.twisted_util import gather_results
 
12
from landscape.lib.fetch import fetch_async
10
13
 
11
14
from landscape.package.taskhandler import PackageTaskHandler, run_task_handler
12
15
from landscape.package.store import UnknownHashIDRequest
17
20
 
18
21
 
19
22
class PackageReporter(PackageTaskHandler):
 
23
    """Report information about the system packages.
20
24
 
 
25
    @cvar queue_name: Name of the task queue to pick tasks from.
 
26
    @cvar smart_update_interval: Time interval in minutes to pass to
 
27
        the C{--after} command line option of C{smart-update}.
 
28
    """
21
29
    queue_name = "reporter"
 
30
    smart_update_interval = 60
 
31
    smart_update_filename = "/usr/lib/landscape/smart-update"
22
32
 
23
33
    def run(self):
24
34
        result = Deferred()
25
35
 
26
 
        # First, handle any queued tasks.
 
36
        # Run smart-update before anything else, to make sure that
 
37
        # the SmartFacade will load freshly updated channels
 
38
        result.addCallback(lambda x: self.run_smart_update())
 
39
 
 
40
        # If the appropriate hash=>id db is not there, fetch it
 
41
        result.addCallback(lambda x: self.fetch_hash_id_db())
 
42
 
 
43
        # Attach the hash=>id database if available
 
44
        result.addCallback(lambda x: self.use_hash_id_db())
 
45
 
 
46
        # Now, handle any queued tasks.
27
47
        result.addCallback(lambda x: self.handle_tasks())
28
48
 
29
49
        # Then, remove any expired hash=>id translation requests.
38
58
        result.callback(None)
39
59
        return result
40
60
 
 
61
    def fetch_hash_id_db(self):
 
62
        """
 
63
        Fetch the appropriate pre-canned database of hash=>id mappings
 
64
        from the server. If the database is already present, it won't
 
65
        be downloaded twice.
 
66
 
 
67
        The format of the database filename is <uuid>_<codename>_<arch>,
 
68
        and it will be downloaded from the HTTP directory set in
 
69
        config.package_hash_id_url, or config.url/hash-id-databases if
 
70
        the former is not set.
 
71
 
 
72
        Fetch failures are handled gracefully and logged as appropriate.
 
73
        """
 
74
 
 
75
        def fetch_it(hash_id_db_filename):
 
76
 
 
77
            if hash_id_db_filename is None:
 
78
                # Couldn't determine which hash=>id database to fetch,
 
79
                # just ignore the failure and go on
 
80
                return
 
81
 
 
82
            if os.path.exists(hash_id_db_filename):
 
83
                # We don't download twice
 
84
                return
 
85
 
 
86
            base_url = self._get_hash_id_db_base_url()
 
87
            if not base_url:
 
88
                logging.warning("Can't determine the hash=>id database url")
 
89
                return
 
90
 
 
91
            # Cast to str as pycurl doesn't like unicode
 
92
            url = str(base_url + os.path.basename(hash_id_db_filename))
 
93
 
 
94
            def fetch_ok(data):
 
95
                hash_id_db_fd = open(hash_id_db_filename, "w")
 
96
                hash_id_db_fd.write(data)
 
97
                hash_id_db_fd.close()
 
98
                logging.info("Downloaded hash=>id database from %s" % url)
 
99
 
 
100
            def fetch_error(failure):
 
101
                exception = failure.value
 
102
                logging.warning("Couldn't download hash=>id database: %s" %
 
103
                                str(exception))
 
104
 
 
105
            result = fetch_async(url)
 
106
            result.addCallback(fetch_ok)
 
107
            result.addErrback(fetch_error)
 
108
 
 
109
            return result
 
110
 
 
111
        result = self._determine_hash_id_db_filename()
 
112
        result.addCallback(fetch_it)
 
113
        return result
 
114
 
 
115
    def _get_hash_id_db_base_url(self):
 
116
 
 
117
        base_url = self._config.get("package_hash_id_url")
 
118
 
 
119
        if not base_url:
 
120
 
 
121
            if not self._config.get("url"):
 
122
                # We really have no idea where to download from
 
123
                return None
 
124
 
 
125
            # If config.url is http://host:123/path/to/message-system
 
126
            # then we'll use http://host:123/path/to/hash-id-databases
 
127
            base_url = urlparse.urljoin(self._config.url.rstrip("/"),
 
128
                                        "hash-id-databases")
 
129
 
 
130
        return base_url.rstrip("/") + "/"
 
131
 
 
132
    def run_smart_update(self):
 
133
        """Run smart-update and log a warning in case of non-zero exit code.
 
134
 
 
135
        @return: a deferred returning (out, err, code)
 
136
        """
 
137
        result = getProcessOutputAndValue(self.smart_update_filename,
 
138
                                          args=("--after", "%d" %
 
139
                                                self.smart_update_interval))
 
140
 
 
141
        def callback((out, err, code)):
 
142
            # smart-update --after N will exit with error code 1 when it
 
143
            # doesn't actually run the update code because to enough time
 
144
            # has passed yet, but we don't actually consider it a failure.
 
145
            smart_failed = False
 
146
            if code != 0 and code != 1:
 
147
                smart_failed = True
 
148
            if code == 1 and err.strip() != "":
 
149
                smart_failed = True
 
150
            if smart_failed:
 
151
                logging.warning("'%s' exited with status %d (%s)" % (
 
152
                    self.smart_update_filename, code, err))
 
153
            logging.debug("'%s' exited with status %d (out='%s', err='%s'" % (
 
154
                self.smart_update_filename, code, out, err))
 
155
            return (out, err, code)
 
156
 
 
157
        result.addCallback(callback)
 
158
        return result
 
159
 
41
160
    def handle_task(self, task):
42
161
        message = task.data
43
162
        if message["type"] == "package-ids":