~jorge/charms/precise/cinder/fix-README

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/fetch/__init__.py

  • Committer: james.page at ubuntu
  • Date: 2014-05-21 09:57:23 UTC
  • mfrom: (35.1.2 cinder)
  • Revision ID: james.page@ubuntu.com-20140521095723-qiulw0yz0q0k1jcd
[tribaal,r=james-page,t=james-page]

Resync helpers to pickup fixes for apt lock races and better block device detection and handling.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
import importlib
 
2
import time
2
3
from yaml import safe_load
3
4
from charmhelpers.core.host import (
4
5
    lsb_release
15
16
import apt_pkg
16
17
import os
17
18
 
 
19
 
18
20
CLOUD_ARCHIVE = """# Ubuntu Cloud Archive
19
21
deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main
20
22
"""
56
58
    'precise-proposed/icehouse': 'precise-proposed/icehouse',
57
59
}
58
60
 
 
61
# The order of this list is very important. Handlers should be listed in from
 
62
# least- to most-specific URL matching.
 
63
FETCH_HANDLERS = (
 
64
    'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
 
65
    'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
 
66
)
 
67
 
 
68
APT_NO_LOCK = 100  # The return code for "couldn't acquire lock" in APT.
 
69
APT_NO_LOCK_RETRY_DELAY = 10  # Wait 10 seconds between apt lock checks.
 
70
APT_NO_LOCK_RETRY_COUNT = 30  # Retry to acquire the lock X times.
 
71
 
 
72
 
 
73
class SourceConfigError(Exception):
 
74
    pass
 
75
 
 
76
 
 
77
class UnhandledSource(Exception):
 
78
    pass
 
79
 
 
80
 
 
81
class AptLockError(Exception):
 
82
    pass
 
83
 
 
84
 
 
85
class BaseFetchHandler(object):
 
86
 
 
87
    """Base class for FetchHandler implementations in fetch plugins"""
 
88
 
 
89
    def can_handle(self, source):
 
90
        """Returns True if the source can be handled. Otherwise returns
 
91
        a string explaining why it cannot"""
 
92
        return "Wrong source type"
 
93
 
 
94
    def install(self, source):
 
95
        """Try to download and unpack the source. Return the path to the
 
96
        unpacked files or raise UnhandledSource."""
 
97
        raise UnhandledSource("Wrong source type {}".format(source))
 
98
 
 
99
    def parse_url(self, url):
 
100
        return urlparse(url)
 
101
 
 
102
    def base_url(self, url):
 
103
        """Return url without querystring or fragment"""
 
104
        parts = list(self.parse_url(url))
 
105
        parts[4:] = ['' for i in parts[4:]]
 
106
        return urlunparse(parts)
 
107
 
59
108
 
60
109
def filter_installed_packages(packages):
61
110
    """Returns a list of packages that require installation"""
62
111
    apt_pkg.init()
 
112
 
 
113
    # Tell apt to build an in-memory cache to prevent race conditions (if
 
114
    # another process is already building the cache).
 
115
    apt_pkg.config.set("Dir::Cache::pkgcache", "")
 
116
 
63
117
    cache = apt_pkg.Cache()
64
118
    _pkgs = []
65
119
    for package in packages:
87
141
        cmd.extend(packages)
88
142
    log("Installing {} with options: {}".format(packages,
89
143
                                                options))
90
 
    env = os.environ.copy()
91
 
    if 'DEBIAN_FRONTEND' not in env:
92
 
        env['DEBIAN_FRONTEND'] = 'noninteractive'
93
 
 
94
 
    if fatal:
95
 
        subprocess.check_call(cmd, env=env)
96
 
    else:
97
 
        subprocess.call(cmd, env=env)
 
144
    _run_apt_command(cmd, fatal)
98
145
 
99
146
 
100
147
def apt_upgrade(options=None, fatal=False, dist=False):
109
156
    else:
110
157
        cmd.append('upgrade')
111
158
    log("Upgrading with options: {}".format(options))
112
 
 
113
 
    env = os.environ.copy()
114
 
    if 'DEBIAN_FRONTEND' not in env:
115
 
        env['DEBIAN_FRONTEND'] = 'noninteractive'
116
 
 
117
 
    if fatal:
118
 
        subprocess.check_call(cmd, env=env)
119
 
    else:
120
 
        subprocess.call(cmd, env=env)
 
159
    _run_apt_command(cmd, fatal)
121
160
 
122
161
 
123
162
def apt_update(fatal=False):
124
163
    """Update local apt cache"""
125
164
    cmd = ['apt-get', 'update']
126
 
    if fatal:
127
 
        subprocess.check_call(cmd)
128
 
    else:
129
 
        subprocess.call(cmd)
 
165
    _run_apt_command(cmd, fatal)
130
166
 
131
167
 
132
168
def apt_purge(packages, fatal=False):
137
173
    else:
138
174
        cmd.extend(packages)
139
175
    log("Purging {}".format(packages))
140
 
    if fatal:
141
 
        subprocess.check_call(cmd)
142
 
    else:
143
 
        subprocess.call(cmd)
 
176
    _run_apt_command(cmd, fatal)
144
177
 
145
178
 
146
179
def apt_hold(packages, fatal=False):
151
184
    else:
152
185
        cmd.extend(packages)
153
186
    log("Holding {}".format(packages))
 
187
 
154
188
    if fatal:
155
189
        subprocess.check_call(cmd)
156
190
    else:
188
222
                               key])
189
223
 
190
224
 
191
 
class SourceConfigError(Exception):
192
 
    pass
193
 
 
194
 
 
195
225
def configure_sources(update=False,
196
226
                      sources_var='install_sources',
197
227
                      keys_var='install_keys'):
224
254
    if update:
225
255
        apt_update(fatal=True)
226
256
 
227
 
# The order of this list is very important. Handlers should be listed in from
228
 
# least- to most-specific URL matching.
229
 
FETCH_HANDLERS = (
230
 
    'charmhelpers.fetch.archiveurl.ArchiveUrlFetchHandler',
231
 
    'charmhelpers.fetch.bzrurl.BzrUrlFetchHandler',
232
 
)
233
 
 
234
 
 
235
 
class UnhandledSource(Exception):
236
 
    pass
237
 
 
238
257
 
239
258
def install_remote(source):
240
259
    """
265
284
    return install_remote(source)
266
285
 
267
286
 
268
 
class BaseFetchHandler(object):
269
 
 
270
 
    """Base class for FetchHandler implementations in fetch plugins"""
271
 
 
272
 
    def can_handle(self, source):
273
 
        """Returns True if the source can be handled. Otherwise returns
274
 
        a string explaining why it cannot"""
275
 
        return "Wrong source type"
276
 
 
277
 
    def install(self, source):
278
 
        """Try to download and unpack the source. Return the path to the
279
 
        unpacked files or raise UnhandledSource."""
280
 
        raise UnhandledSource("Wrong source type {}".format(source))
281
 
 
282
 
    def parse_url(self, url):
283
 
        return urlparse(url)
284
 
 
285
 
    def base_url(self, url):
286
 
        """Return url without querystring or fragment"""
287
 
        parts = list(self.parse_url(url))
288
 
        parts[4:] = ['' for i in parts[4:]]
289
 
        return urlunparse(parts)
290
 
 
291
 
 
292
287
def plugins(fetch_handlers=None):
293
288
    if not fetch_handlers:
294
289
        fetch_handlers = FETCH_HANDLERS
306
301
            log("FetchHandler {} not found, skipping plugin".format(
307
302
                handler_name))
308
303
    return plugin_list
 
304
 
 
305
 
 
306
def _run_apt_command(cmd, fatal=False):
 
307
    """
 
308
    Run an APT command, checking output and retrying if the fatal flag is set
 
309
    to True.
 
310
 
 
311
    :param: cmd: str: The apt command to run.
 
312
    :param: fatal: bool: Whether the command's output should be checked and
 
313
        retried.
 
314
    """
 
315
    env = os.environ.copy()
 
316
 
 
317
    if 'DEBIAN_FRONTEND' not in env:
 
318
        env['DEBIAN_FRONTEND'] = 'noninteractive'
 
319
 
 
320
    if fatal:
 
321
        retry_count = 0
 
322
        result = None
 
323
 
 
324
        # If the command is considered "fatal", we need to retry if the apt
 
325
        # lock was not acquired.
 
326
 
 
327
        while result is None or result == APT_NO_LOCK:
 
328
            try:
 
329
                result = subprocess.check_call(cmd, env=env)
 
330
            except subprocess.CalledProcessError, e:
 
331
                retry_count = retry_count + 1
 
332
                if retry_count > APT_NO_LOCK_RETRY_COUNT:
 
333
                    raise
 
334
                result = e.returncode
 
335
                log("Couldn't acquire DPKG lock. Will retry in {} seconds."
 
336
                    "".format(APT_NO_LOCK_RETRY_DELAY))
 
337
                time.sleep(APT_NO_LOCK_RETRY_DELAY)
 
338
 
 
339
    else:
 
340
        subprocess.call(cmd, env=env)