~oddbloke/ubuntu/precise/cloud-init/lp1374600

« back to all changes in this revision

Viewing changes to .pc/lp-1100491-fix-broken-add-sources.patch/cloudinit/CloudConfig/cc_apt_update_upgrade.py

  • Committer: Package Import Robot
  • Author(s): Scott Moser
  • Date: 2013-01-16 17:19:50 UTC
  • Revision ID: package-import@ubuntu.com-20130116171950-oqxyj3sddei59m2o
Tags: 0.6.3-0ubuntu1.4
lp-1100491-fix-broken-add-sources.patch: fix use of
'apt_sources' in cloud-config.  (LP: #1100491)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# vi: ts=4 expandtab
 
2
#
 
3
#    Copyright (C) 2009-2010 Canonical Ltd.
 
4
#    Copyright (C) 2012 Hewlett-Packard Development Company, L.P.
 
5
#
 
6
#    Author: Scott Moser <scott.moser@canonical.com>
 
7
#    Author: Juerg Haefliger <juerg.haefliger@hp.com>
 
8
#
 
9
#    This program is free software: you can redistribute it and/or modify
 
10
#    it under the terms of the GNU General Public License version 3, as
 
11
#    published by the Free Software Foundation.
 
12
#
 
13
#    This program is distributed in the hope that it will be useful,
 
14
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
#    GNU General Public License for more details.
 
17
#
 
18
#    You should have received a copy of the GNU General Public License
 
19
#    along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
20
 
 
21
import cloudinit.util as util
 
22
import subprocess
 
23
import traceback
 
24
import os
 
25
import glob
 
26
import cloudinit.CloudConfig as cc
 
27
import re
 
28
 
 
29
 
 
30
def handle(name, cfg, cloud, log, _args):
 
31
    update = util.get_cfg_option_bool(cfg, 'apt_update', False)
 
32
    upgrade = util.get_cfg_option_bool(cfg, 'apt_upgrade', False)
 
33
 
 
34
    release = get_release()
 
35
 
 
36
    mirrors = find_apt_mirror_info(cloud, cfg, log)
 
37
 
 
38
    if not mirrors or "primary" not in mirrors:
 
39
         log.debug(("Skipping module named %s,"
 
40
                    " no package 'mirror' located"), name)
 
41
         return
 
42
 
 
43
    # backwards compatibility
 
44
    mirror = mirrors["primary"]
 
45
    mirrors["mirror"] = mirror
 
46
 
 
47
    log.debug("mirror info: %s" % mirrors)
 
48
 
 
49
    if not util.get_cfg_option_bool(cfg, \
 
50
        'apt_preserve_sources_list', False):
 
51
        generate_sources_list(release, mirrors)
 
52
        old_mirrors = cfg.get('apt_old_mirrors',
 
53
                              {"primary": "archive.ubuntu.com/ubuntu",
 
54
                               "security": "security.ubuntu.com/ubuntu"})
 
55
        rename_apt_lists(old_mirrors, mirrors)
 
56
 
 
57
    # set up proxy
 
58
    proxy = cfg.get("apt_proxy", None)
 
59
    proxy_filename = "/etc/apt/apt.conf.d/95cloud-init-proxy"
 
60
    if proxy:
 
61
        try:
 
62
            contents = "Acquire::HTTP::Proxy \"%s\";\n"
 
63
            with open(proxy_filename, "w") as fp:
 
64
                fp.write(contents % proxy)
 
65
        except Exception as e:
 
66
            log.warn("Failed to write proxy to %s" % proxy_filename)
 
67
    elif os.path.isfile(proxy_filename):
 
68
        os.unlink(proxy_filename)
 
69
 
 
70
    # process 'apt_sources'
 
71
    if 'apt_sources' in cfg:
 
72
        params = mirrors
 
73
        params['RELEASE'] = release
 
74
        params['MIRROR'] = mirror
 
75
        errors = add_sources(cloud, cfg['apt_sources'], params)
 
76
        for e in errors:
 
77
            log.warn("Source Error: %s\n" % ':'.join(e))
 
78
 
 
79
    dconf_sel = util.get_cfg_option_str(cfg, 'debconf_selections', False)
 
80
    if dconf_sel:
 
81
        log.debug("setting debconf selections per cloud config")
 
82
        try:
 
83
            util.subp(('debconf-set-selections', '-'), dconf_sel)
 
84
        except:
 
85
            log.error("Failed to run debconf-set-selections")
 
86
            log.debug(traceback.format_exc())
 
87
 
 
88
    pkglist = util.get_cfg_option_list_or_str(cfg, 'packages', [])
 
89
 
 
90
    errors = []
 
91
    if update or len(pkglist) or upgrade:
 
92
        try:
 
93
            cc.update_package_sources()
 
94
        except subprocess.CalledProcessError as e:
 
95
            log.warn("apt-get update failed")
 
96
            log.debug(traceback.format_exc())
 
97
            errors.append(e)
 
98
 
 
99
    if upgrade:
 
100
        try:
 
101
            cc.apt_get("upgrade")
 
102
        except subprocess.CalledProcessError as e:
 
103
            log.warn("apt upgrade failed")
 
104
            log.debug(traceback.format_exc())
 
105
            errors.append(e)
 
106
 
 
107
    if len(pkglist):
 
108
        try:
 
109
            cc.install_packages(pkglist)
 
110
        except subprocess.CalledProcessError as e:
 
111
            log.warn("Failed to install packages: %s " % pkglist)
 
112
            log.debug(traceback.format_exc())
 
113
            errors.append(e)
 
114
 
 
115
    if len(errors):
 
116
        raise errors[0]
 
117
 
 
118
    return(True)
 
119
 
 
120
 
 
121
def mirror2lists_fileprefix(mirror):
 
122
    string = mirror
 
123
    # take of http:// or ftp://
 
124
    if string.endswith("/"):
 
125
        string = string[0:-1]
 
126
    pos = string.find("://")
 
127
    if pos >= 0:
 
128
        string = string[pos + 3:]
 
129
    string = string.replace("/", "_")
 
130
    return string
 
131
 
 
132
 
 
133
def rename_apt_lists(old_mirrors, new_mirrors, lists_d="/var/lib/apt/lists"):
 
134
    for (name, omirror) in old_mirrors.iteritems():
 
135
        nmirror = new_mirrors.get(name)
 
136
        if not nmirror:
 
137
            continue
 
138
        oprefix = os.path.join(lists_d, mirror2lists_fileprefix(omirror))
 
139
        nprefix = os.path.join(lists_d, mirror2lists_fileprefix(nmirror))
 
140
        if oprefix == nprefix:
 
141
            continue
 
142
        olen = len(oprefix)
 
143
        for filename in glob.glob("%s_*" % oprefix):
 
144
            os.rename(filename, "%s%s" % (nprefix, filename[olen:]))
 
145
 
 
146
 
 
147
def get_release():
 
148
    stdout, _stderr = subprocess.Popen(['lsb_release', '-cs'],
 
149
                                       stdout=subprocess.PIPE).communicate()
 
150
    return(str(stdout).strip())
 
151
 
 
152
 
 
153
def generate_sources_list(codename, mirrors):
 
154
    params = {'codename': codename}
 
155
    for k in mirrors:
 
156
        params[k] = mirrors[k]
 
157
    util.render_to_file('sources.list', '/etc/apt/sources.list', params)
 
158
 
 
159
 
 
160
def add_sources(srclist, searchList=None):
 
161
    """
 
162
    add entries in /etc/apt/sources.list.d for each abbreviated
 
163
    sources.list entry in 'srclist'.  When rendering template, also
 
164
    include the values in dictionary searchList
 
165
    """
 
166
    if searchList is None:
 
167
        searchList = {}
 
168
    elst = []
 
169
 
 
170
    for ent in srclist:
 
171
        if 'source' not in ent:
 
172
            elst.append(["", "missing source"])
 
173
            continue
 
174
 
 
175
        source = ent['source']
 
176
        if source.startswith("ppa:"):
 
177
            try:
 
178
                util.subp(["add-apt-repository", source])
 
179
            except:
 
180
                elst.append([source, "add-apt-repository failed"])
 
181
            continue
 
182
 
 
183
        source = util.render_string(source, searchList)
 
184
 
 
185
        if 'filename' not in ent:
 
186
            ent['filename'] = 'cloud_config_sources.list'
 
187
 
 
188
        if not ent['filename'].startswith("/"):
 
189
            ent['filename'] = "%s/%s" % \
 
190
                ("/etc/apt/sources.list.d/", ent['filename'])
 
191
 
 
192
        if ('keyid' in ent and 'key' not in ent):
 
193
            ks = "keyserver.ubuntu.com"
 
194
            if 'keyserver' in ent:
 
195
                ks = ent['keyserver']
 
196
            try:
 
197
                ent['key'] = util.getkeybyid(ent['keyid'], ks)
 
198
            except:
 
199
                elst.append([source, "failed to get key from %s" % ks])
 
200
                continue
 
201
 
 
202
        if 'key' in ent:
 
203
            try:
 
204
                util.subp(('apt-key', 'add', '-'), ent['key'])
 
205
            except:
 
206
                elst.append([source, "failed add key"])
 
207
 
 
208
        try:
 
209
            util.write_file(ent['filename'], source + "\n", omode="ab")
 
210
        except:
 
211
            elst.append([source, "failed write to file %s" % ent['filename']])
 
212
 
 
213
    return(elst)
 
214
 
 
215
 
 
216
def find_apt_mirror_info(cloud, cfg, log):
 
217
    """ find an apt_mirror given the cloud and cfg provided """
 
218
 
 
219
    # TODO: distro and defaults should be configurable
 
220
    distro = "ubuntu"
 
221
 
 
222
    # this is used if cfg['system_info']['package_mirrors'] is not present
 
223
    def_mirror_info = {
 
224
        'ubuntu': {
 
225
            'primary': "http://archive.ubuntu.com/ubuntu",
 
226
            'security': "http://security.ubuntu.com/ubuntu"
 
227
        }
 
228
    }
 
229
    mirror = None
 
230
 
 
231
    # this is less preferred way of specifying mirror preferred would be to
 
232
    # use the distro's search or package_mirror.
 
233
    mirror = cfg.get("apt_mirror", None)
 
234
 
 
235
    search = cfg.get("apt_mirror_search", None)
 
236
    if not mirror and search:
 
237
        mirror = util.search_for_mirror(search)
 
238
 
 
239
    if (not mirror and
 
240
        util.get_cfg_option_bool(cfg, "apt_mirror_search_dns", False)):
 
241
        mydom = ""
 
242
 
 
243
        doms = []
 
244
 
 
245
        # if we have a fqdn, then search its domain portion first
 
246
        (_hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud)
 
247
        mydom = ".".join(fqdn.split(".")[1:])
 
248
        if mydom:
 
249
            doms.append(".%s" % mydom)
 
250
 
 
251
        doms.extend((".localdomain", "",))
 
252
 
 
253
        mirror_list = []
 
254
        mirrorfmt = "http://%s-mirror%s/%s" % (distro, "%s", distro)
 
255
        for post in doms:
 
256
            mirror_list.append(mirrorfmt % (post))
 
257
 
 
258
        mirror = util.search_for_mirror(mirror_list)
 
259
 
 
260
    try:
 
261
        pmirrors = cfg['system_info']['package_mirrors']
 
262
        az = cloud.datasource.get_availability_zone()
 
263
        mirror_info = get_package_mirror_info(package_mirrors=pmirrors,
 
264
                                              availability_zone=az)
 
265
    except Exception as e:
 
266
        util.logexc(log)
 
267
        log.warn("Failed to get mirror info, falling back to default" %
 
268
                 def_mirror_info)
 
269
        mirror_info = def_mirror_info
 
270
 
 
271
    # this is a bit strange.
 
272
    # if mirror is set, then one of the legacy options above set it
 
273
    # but they do not cover security. so we need to get that from
 
274
    # get_package_mirror_info
 
275
    if mirror:
 
276
        mirror_info.update({'primary': mirror})
 
277
 
 
278
    return mirror_info
 
279
 
 
280
## put together from trunk's cloudinit/distros/__init__.py and
 
281
##                           cloudinit/sources/__init__.py
 
282
def get_package_mirror_info(package_mirrors,
 
283
                            availability_zone=None, arch=None):
 
284
    if arch == None:
 
285
        arch = get_primary_arch()
 
286
    arch_info = _get_arch_package_mirror_info(package_mirrors, arch)
 
287
 
 
288
    info = _get_package_mirror_info(mirror_info=arch_info,
 
289
                                    availability_zone=availability_zone)
 
290
    return info
 
291
 
 
292
## taken from trunk's cloudinit/distros/debian.py (Distro)
 
293
def get_primary_arch():
 
294
    (arch, _err) = util.subp(['dpkg', '--print-architecture'])
 
295
    return str(arch).strip()
 
296
 
 
297
## taken from trunk's cloudinit/distros/__init__.py ##
 
298
def _get_package_mirror_info(mirror_info, availability_zone=None,
 
299
                             mirror_filter=util.search_for_mirror):
 
300
    # given a arch specific 'mirror_info' entry (from package_mirrors)
 
301
    # search through the 'search' entries, and fallback appropriately
 
302
    # return a dict with only {name: mirror} entries.
 
303
 
 
304
    ec2_az_re = ("^[a-z][a-z]-(%s)-[1-9][0-9]*[a-z]$" %
 
305
        "north|northeast|east|southeast|south|southwest|west|northwest")
 
306
 
 
307
    subst = {}
 
308
    if availability_zone:
 
309
        subst['availability_zone'] = availability_zone
 
310
 
 
311
    if availability_zone and re.match(ec2_az_re, availability_zone):
 
312
        subst['ec2_region'] = "%s" % availability_zone[0:-1]
 
313
 
 
314
    results = {}
 
315
    for (name, mirror) in mirror_info.get('failsafe', {}).iteritems():
 
316
        results[name] = mirror
 
317
 
 
318
    for (name, searchlist) in mirror_info.get('search', {}).iteritems():
 
319
        mirrors = []
 
320
        for tmpl in searchlist:
 
321
            try:
 
322
                mirrors.append(tmpl % subst)
 
323
            except KeyError:
 
324
                pass
 
325
 
 
326
        found = mirror_filter(mirrors)
 
327
        if found:
 
328
            results[name] = found
 
329
 
 
330
    #LOG.debug("filtered distro mirror info: %s" % results)
 
331
 
 
332
    return results
 
333
 
 
334
## taken from trunk's cloudinit/distros/__init__.py
 
335
def _get_arch_package_mirror_info(package_mirrors, arch):
 
336
    # pull out the specific arch from a 'package_mirrors' config option
 
337
    default = None
 
338
    for item in package_mirrors:
 
339
        arches = item.get("arches")
 
340
        if arch in arches:
 
341
            return item
 
342
        if "default" in arches:
 
343
            default = item
 
344
    return default