Merge lp:~smoser/cloud-init/mirror-rework into lp:~cloud-init-dev/cloud-init/trunk

Proposed by Scott Moser
Status: Merged
Merged at revision: 630
Proposed branch: lp:~smoser/cloud-init/mirror-rework
Merge into: lp:~cloud-init-dev/cloud-init/trunk
Diff against target: 591 lines (+309/-108)
11 files modified
ChangeLog (+4/-0)
cloudinit/cloud.py (+0/-3)
cloudinit/config/cc_apt_update_upgrade.py (+74/-57)
cloudinit/distros/__init__.py (+71/-2)
cloudinit/distros/debian.py (+4/-0)
cloudinit/sources/DataSourceCloudStack.py (+2/-1)
cloudinit/sources/DataSourceEc2.py (+6/-34)
cloudinit/sources/__init__.py (+7/-3)
config/cloud.cfg (+14/-2)
templates/sources.list.tmpl (+6/-6)
tests/unittests/test_distros/test_generic.py (+121/-0)
To merge this branch: bzr merge lp:~smoser/cloud-init/mirror-rework
Reviewer Review Type Date Requested Status
cloud-init Commiters Pending
Review via email: mp+120852@code.launchpad.net

Commit message

rework package mirror selection

There are several changes here.
 * Datasource now has a 'availability_zone' getter.
 * get_package_mirror_info
   * Datasource convenience 'get_package_mirror_info' that calls
     the configured distro, and passes it the availability-zone
   * distro has a get_package_mirror_info method
   * get_package_mirror_info returns a dict that of name:mirror
     this is to facilitate use of 'security' and 'primary' archive.
   * this supports searching based on templates. Any template
     that references undefined values is skipped. These templates
     can contain 'availability_zone' (LP: #1037727)
   * distro's mirrors can be arch specific (LP: #1028501)
 * rename_apt_lists supports the "mirror_info" rather than single mirror
 * generate_sources_list supports mirror_info, and as a result, the
   ubuntu mirrors reference '$security' rather than security (LP: #1006963)
 * remove the DataSourceEc2 specific mirror selection, but instead
   rely on the above filtering, and the fact that 'ec2_region' is only
   defined if the availability_zone looks like a ec2 az.

To post a comment you must log in.
lp:~smoser/cloud-init/mirror-rework updated
634. By Scott Moser

improve the check for "uses unknown key" in mirror templates

instead of substituting and then checking for presense of a unlikely to
occur string, this only adds to the search list if there is no KeyError
raised.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2012-08-21 01:27:04 +0000
3+++ ChangeLog 2012-08-22 19:21:17 +0000
4@@ -1,4 +1,8 @@
5 0.7.0:
6+ - allow distro mirror selection to include availability-zone (LP: #1037727)
7+ - allow arch specific mirror selection (select ports.ubuntu.com on arm)
8+ LP: #1028501
9+ - allow specification of security mirrors (LP: #1006963)
10 - add the 'None' datasource (LP: #906669), which will allow jobs
11 to run even if there is no "real" datasource found.
12 - write ssh authorized keys to console, ssh_authkey_fingerprints
13
14=== modified file 'cloudinit/cloud.py'
15--- cloudinit/cloud.py 2012-06-21 16:12:16 +0000
16+++ cloudinit/cloud.py 2012-08-22 19:21:17 +0000
17@@ -82,9 +82,6 @@
18 def get_locale(self):
19 return self.datasource.get_locale()
20
21- def get_local_mirror(self):
22- return self.datasource.get_local_mirror()
23-
24 def get_hostname(self, fqdn=False):
25 return self.datasource.get_hostname(fqdn=fqdn)
26
27
28=== modified file 'cloudinit/config/cc_apt_update_upgrade.py'
29--- cloudinit/config/cc_apt_update_upgrade.py 2012-08-22 18:12:32 +0000
30+++ cloudinit/config/cc_apt_update_upgrade.py 2012-08-22 19:21:17 +0000
31@@ -50,20 +50,25 @@
32 upgrade = util.get_cfg_option_bool(cfg, 'apt_upgrade', False)
33
34 release = get_release()
35- mirror = find_apt_mirror(cloud, cfg)
36- if not mirror:
37+ mirrors = find_apt_mirror_info(cloud, cfg)
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- log.debug("Selected mirror at: %s" % mirror)
44+ # backwards compatibility
45+ mirror = mirrors["primary"]
46+ mirrors["mirror"] = mirror
47+
48+ log.debug("mirror info: %s" % mirrors)
49
50 if not util.get_cfg_option_bool(cfg,
51 'apt_preserve_sources_list', False):
52- generate_sources_list(release, mirror, cloud, log)
53- old_mir = util.get_cfg_option_str(cfg, 'apt_old_mirror',
54- "archive.ubuntu.com/ubuntu")
55- rename_apt_lists(old_mir, mirror)
56+ generate_sources_list(release, mirrors, cloud, log)
57+ old_mirrors = cfg.get('apt_old_mirrors',
58+ {"primary": "archive.ubuntu.com/ubuntu",
59+ "security": "security.ubuntu.com/ubuntu"})
60+ rename_apt_lists(old_mirrors, mirrors)
61
62 # Set up any apt proxy
63 proxy = cfg.get("apt_proxy", None)
64@@ -81,8 +86,10 @@
65
66 # Process 'apt_sources'
67 if 'apt_sources' in cfg:
68- errors = add_sources(cloud, cfg['apt_sources'],
69- {'MIRROR': mirror, 'RELEASE': release})
70+ params = mirrors
71+ params['RELEASE'] = release
72+ params['MIRROR'] = mirror
73+ errors = add_sources(cloud, cfg['apt_sources'], params)
74 for e in errors:
75 log.warn("Source Error: %s", ':'.join(e))
76
77@@ -146,15 +153,18 @@
78 return string
79
80
81-def rename_apt_lists(omirror, new_mirror, lists_d="/var/lib/apt/lists"):
82- oprefix = os.path.join(lists_d, mirror2lists_fileprefix(omirror))
83- nprefix = os.path.join(lists_d, mirror2lists_fileprefix(new_mirror))
84- if oprefix == nprefix:
85- return
86- olen = len(oprefix)
87- for filename in glob.glob("%s_*" % oprefix):
88- # TODO(harlowja) use the cloud.paths.join...
89- util.rename(filename, "%s%s" % (nprefix, filename[olen:]))
90+def rename_apt_lists(old_mirrors, new_mirrors, lists_d="/var/lib/apt/lists"):
91+ for (name, omirror) in old_mirrors.iteritems():
92+ nmirror = new_mirrors.get(name)
93+ if not nmirror:
94+ continue
95+ oprefix = os.path.join(lists_d, mirror2lists_fileprefix(omirror))
96+ nprefix = os.path.join(lists_d, mirror2lists_fileprefix(nmirror))
97+ if oprefix == nprefix:
98+ continue
99+ olen = len(oprefix)
100+ for filename in glob.glob("%s_*" % oprefix):
101+ util.rename(filename, "%s%s" % (nprefix, filename[olen:]))
102
103
104 def get_release():
105@@ -162,14 +172,17 @@
106 return stdout.strip()
107
108
109-def generate_sources_list(codename, mirror, cloud, log):
110+def generate_sources_list(codename, mirrors, cloud, log):
111 template_fn = cloud.get_template_filename('sources.list')
112- if template_fn:
113- params = {'mirror': mirror, 'codename': codename}
114- out_fn = cloud.paths.join(False, '/etc/apt/sources.list')
115- templater.render_to_file(template_fn, out_fn, params)
116- else:
117+ if not template_fn:
118 log.warn("No template found, not rendering /etc/apt/sources.list")
119+ return
120+
121+ params = {'codename': codename}
122+ for k in mirrors:
123+ params[k] = mirrors[k]
124+ out_fn = cloud.paths.join(False, '/etc/apt/sources.list')
125+ templater.render_to_file(template_fn, out_fn, params)
126
127
128 def add_sources(cloud, srclist, template_params=None):
129@@ -231,43 +244,47 @@
130 return errorlist
131
132
133-def find_apt_mirror(cloud, cfg):
134+def find_apt_mirror_info(cloud, cfg):
135 """find an apt_mirror given the cloud and cfg provided."""
136
137 mirror = None
138
139- cfg_mirror = cfg.get("apt_mirror", None)
140- if cfg_mirror:
141- mirror = cfg["apt_mirror"]
142- elif "apt_mirror_search" in cfg:
143- mirror = util.search_for_mirror(cfg['apt_mirror_search'])
144- else:
145- mirror = cloud.get_local_mirror()
146-
147+ # this is less preferred way of specifying mirror preferred would be to
148+ # use the distro's search or package_mirror.
149+ mirror = cfg.get("apt_mirror", None)
150+
151+ search = cfg.get("apt_mirror_search", None)
152+ if not mirror and search:
153+ mirror = util.search_for_mirror(search)
154+
155+ if (not mirror and
156+ util.get_cfg_option_bool(cfg, "apt_mirror_search_dns", False)):
157 mydom = ""
158-
159 doms = []
160
161- if not mirror:
162- # if we have a fqdn, then search its domain portion first
163- (_hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud)
164- mydom = ".".join(fqdn.split(".")[1:])
165- if mydom:
166- doms.append(".%s" % mydom)
167-
168- if (not mirror and
169- util.get_cfg_option_bool(cfg, "apt_mirror_search_dns", False)):
170- doms.extend((".localdomain", "",))
171-
172- mirror_list = []
173- distro = cloud.distro.name
174- mirrorfmt = "http://%s-mirror%s/%s" % (distro, "%s", distro)
175- for post in doms:
176- mirror_list.append(mirrorfmt % (post))
177-
178- mirror = util.search_for_mirror(mirror_list)
179-
180- if not mirror:
181- mirror = cloud.distro.get_package_mirror()
182-
183- return mirror
184+ # if we have a fqdn, then search its domain portion first
185+ (_hostname, fqdn) = util.get_hostname_fqdn(cfg, cloud)
186+ mydom = ".".join(fqdn.split(".")[1:])
187+ if mydom:
188+ doms.append(".%s" % mydom)
189+
190+ doms.extend((".localdomain", "",))
191+
192+ mirror_list = []
193+ distro = cloud.distro.name
194+ mirrorfmt = "http://%s-mirror%s/%s" % (distro, "%s", distro)
195+ for post in doms:
196+ mirror_list.append(mirrorfmt % (post))
197+
198+ mirror = util.search_for_mirror(mirror_list)
199+
200+ mirror_info = cloud.datasource.get_package_mirror_info()
201+
202+ # this is a bit strange.
203+ # if mirror is set, then one of the legacy options above set it
204+ # but they do not cover security. so we need to get that from
205+ # get_package_mirror_info
206+ if mirror:
207+ mirror_info.update({'primary': mirror})
208+
209+ return mirror_info
210
211=== modified file 'cloudinit/distros/__init__.py'
212--- cloudinit/distros/__init__.py 2012-08-22 18:12:32 +0000
213+++ cloudinit/distros/__init__.py 2012-08-22 19:21:17 +0000
214@@ -23,6 +23,8 @@
215 from StringIO import StringIO
216
217 import abc
218+import os
219+import re
220
221 from cloudinit import importer
222 from cloudinit import log as logging
223@@ -75,8 +77,26 @@
224 def update_package_sources(self):
225 raise NotImplementedError()
226
227- def get_package_mirror(self):
228- return self.get_option('package_mirror')
229+ def get_primary_arch(self):
230+ arch = os.uname[4]
231+ if arch in ("i386", "i486", "i586", "i686"):
232+ return "i386"
233+ return arch
234+
235+ def _get_arch_package_mirror_info(self, arch=None):
236+ mirror_info = self.get_option("package_mirrors", None)
237+ if arch == None:
238+ arch = self.get_primary_arch()
239+ return _get_arch_package_mirror_info(mirror_info, arch)
240+
241+ def get_package_mirror_info(self, arch=None,
242+ availability_zone=None):
243+ # this resolves the package_mirrors config option
244+ # down to a single dict of {mirror_name: mirror_url}
245+ arch_info = self._get_arch_package_mirror_info(arch)
246+
247+ return _get_package_mirror_info(availability_zone=availability_zone,
248+ mirror_info=arch_info)
249
250 def apply_network(self, settings, bring_up=True):
251 # Write it out
252@@ -151,6 +171,55 @@
253 return False
254
255
256+def _get_package_mirror_info(mirror_info, availability_zone=None,
257+ mirror_filter=util.search_for_mirror):
258+ # given a arch specific 'mirror_info' entry (from package_mirrors)
259+ # search through the 'search' entries, and fallback appropriately
260+ # return a dict with only {name: mirror} entries.
261+
262+ ec2_az_re = ("^[a-z][a-z]-(%s)-[1-9][0-9]*[a-z]$" %
263+ "north|northeast|east|southeast|south|southwest|west|northwest")
264+
265+ subst = {}
266+ if availability_zone:
267+ subst['availability_zone'] = availability_zone
268+
269+ if availability_zone and re.match(ec2_az_re, availability_zone):
270+ subst['ec2_region'] = "%s" % availability_zone[0:-1]
271+
272+ results = {}
273+ for (name, mirror) in mirror_info.get('failsafe', {}).iteritems():
274+ results[name] = mirror
275+
276+ for (name, searchlist) in mirror_info.get('search', {}).iteritems():
277+ mirrors = []
278+ for tmpl in searchlist:
279+ try:
280+ mirrors.append(tmpl % subst)
281+ except KeyError:
282+ pass
283+
284+ found = mirror_filter(mirrors)
285+ if found:
286+ results[name] = found
287+
288+ LOG.debug("filtered distro mirror info: %s" % results)
289+
290+ return results
291+
292+
293+def _get_arch_package_mirror_info(package_mirrors, arch):
294+ # pull out the specific arch from a 'package_mirrors' config option
295+ default = None
296+ for item in package_mirrors:
297+ arches = item.get("arches")
298+ if arch in arches:
299+ return item
300+ if "default" in arches:
301+ default = item
302+ return default
303+
304+
305 def fetch(name):
306 locs = importer.find_module(name,
307 ['', __name__],
308
309=== modified file 'cloudinit/distros/debian.py'
310--- cloudinit/distros/debian.py 2012-06-30 00:06:32 +0000
311+++ cloudinit/distros/debian.py 2012-08-22 19:21:17 +0000
312@@ -147,3 +147,7 @@
313 def update_package_sources(self):
314 self._runner.run("update-sources", self.package_command,
315 ["update"], freq=PER_INSTANCE)
316+
317+ def get_primary_arch(self):
318+ (arch, _err) = util.subp(['dpkg', '--print-architecture'])
319+ return str(arch).strip()
320
321=== modified file 'cloudinit/sources/DataSourceCloudStack.py'
322--- cloudinit/sources/DataSourceCloudStack.py 2012-08-22 18:12:32 +0000
323+++ cloudinit/sources/DataSourceCloudStack.py 2012-08-22 19:21:17 +0000
324@@ -131,7 +131,8 @@
325 def get_instance_id(self):
326 return self.metadata['instance-id']
327
328- def get_availability_zone(self):
329+ @property
330+ def availability_zone(self):
331 return self.metadata['availability-zone']
332
333
334
335=== modified file 'cloudinit/sources/DataSourceEc2.py'
336--- cloudinit/sources/DataSourceEc2.py 2012-07-16 20:16:27 +0000
337+++ cloudinit/sources/DataSourceEc2.py 2012-08-22 19:21:17 +0000
338@@ -83,40 +83,6 @@
339 def get_availability_zone(self):
340 return self.metadata['placement']['availability-zone']
341
342- def get_local_mirror(self):
343- return self.get_mirror_from_availability_zone()
344-
345- def get_mirror_from_availability_zone(self, availability_zone=None):
346- # Return type None indicates there is no cloud specific mirror
347- # Availability is like 'us-west-1b' or 'eu-west-1a'
348- if availability_zone is None:
349- availability_zone = self.get_availability_zone()
350-
351- if self.is_vpc():
352- return None
353-
354- if not availability_zone:
355- return None
356-
357- mirror_tpl = self.distro.get_option('package_mirror_ec2_template',
358- None)
359-
360- if mirror_tpl is None:
361- return None
362-
363- # in EC2, the 'region' is 'us-east-1' if 'zone' is 'us-east-1a'
364- tpl_params = {
365- 'zone': availability_zone.strip(),
366- 'region': availability_zone[:-1]
367- }
368- mirror_url = mirror_tpl % (tpl_params)
369-
370- found = util.search_for_mirror([mirror_url])
371- if found is not None:
372- return mirror_url
373-
374- return None
375-
376 def _get_url_settings(self):
377 mcfg = self.ds_cfg
378 if not mcfg:
379@@ -255,6 +221,12 @@
380 return True
381 return False
382
383+ @property
384+ def availability_zone(self):
385+ try:
386+ return self.metadata['placement']['availability-zone']
387+ except KeyError:
388+ return None
389
390 # Used to match classes to dependencies
391 datasources = [
392
393=== modified file 'cloudinit/sources/__init__.py'
394--- cloudinit/sources/__init__.py 2012-08-20 05:28:14 +0000
395+++ cloudinit/sources/__init__.py 2012-08-22 19:21:17 +0000
396@@ -117,9 +117,9 @@
397 def get_locale(self):
398 return 'en_US.UTF-8'
399
400- def get_local_mirror(self):
401- # ??
402- return None
403+ @property
404+ def availability_zone(self):
405+ return self.metadata.get('availability-zone')
406
407 def get_instance_id(self):
408 if not self.metadata or 'instance-id' not in self.metadata:
409@@ -166,6 +166,10 @@
410 else:
411 return hostname
412
413+ def get_package_mirror_info(self):
414+ return self.distro.get_package_mirror_info(
415+ availability_zone=self.availability_zone)
416+
417
418 def find_source(sys_cfg, distro, paths, ds_deps, cfg_list, pkg_list):
419 ds_list = list_sources(cfg_list, ds_deps, pkg_list)
420
421=== modified file 'config/cloud.cfg'
422--- config/cloud.cfg 2012-08-20 21:11:46 +0000
423+++ config/cloud.cfg 2012-08-22 19:21:17 +0000
424@@ -74,6 +74,18 @@
425 cloud_dir: /var/lib/cloud/
426 templates_dir: /etc/cloud/templates/
427 upstart_dir: /etc/init/
428- package_mirror: http://archive.ubuntu.com/ubuntu
429- package_mirror_ec2_template: http://%(region)s.ec2.archive.ubuntu.com/ubuntu/
430+ package_mirrors:
431+ - arches: [i386, amd64]
432+ failsafe:
433+ primary: http://archive.ubuntu.com/ubuntu
434+ security: http://security.ubuntu.com/ubuntu
435+ search:
436+ primary:
437+ - http://%(ec2_region)s.ec2.archive.ubuntu.com/ubuntu/
438+ - http://%(availability_zone)s.clouds.archive.ubuntu.com/ubuntu/
439+ security: []
440+ - arches: [armhf, armel, default]
441+ failsafe:
442+ primary: http://ports.ubuntu.com/ubuntu
443+ security: http://ports.ubuntu.com/ubuntu
444 ssh_svcname: ssh
445
446=== modified file 'templates/sources.list.tmpl'
447--- templates/sources.list.tmpl 2012-07-09 20:41:45 +0000
448+++ templates/sources.list.tmpl 2012-08-22 19:21:17 +0000
449@@ -52,9 +52,9 @@
450 # deb http://archive.canonical.com/ubuntu $codename partner
451 # deb-src http://archive.canonical.com/ubuntu $codename partner
452
453-deb http://security.ubuntu.com/ubuntu $codename-security main
454-deb-src http://security.ubuntu.com/ubuntu $codename-security main
455-deb http://security.ubuntu.com/ubuntu $codename-security universe
456-deb-src http://security.ubuntu.com/ubuntu $codename-security universe
457-# deb http://security.ubuntu.com/ubuntu $codename-security multiverse
458-# deb-src http://security.ubuntu.com/ubuntu $codename-security multiverse
459+deb $security $codename-security main
460+deb-src $security $codename-security main
461+deb $security $codename-security universe
462+deb-src $security $codename-security universe
463+# deb $security $codename-security multiverse
464+# deb-src $security $codename-security multiverse
465
466=== added directory 'tests/unittests/test_distros'
467=== added file 'tests/unittests/test_distros/test_generic.py'
468--- tests/unittests/test_distros/test_generic.py 1970-01-01 00:00:00 +0000
469+++ tests/unittests/test_distros/test_generic.py 2012-08-22 19:21:17 +0000
470@@ -0,0 +1,121 @@
471+from mocker import MockerTestCase
472+
473+from cloudinit import distros
474+
475+unknown_arch_info = {
476+ 'arches': ['default'],
477+ 'failsafe': {'primary': 'http://fs-primary-default',
478+ 'security': 'http://fs-security-default'}
479+}
480+
481+package_mirrors = [
482+ {'arches': ['i386', 'amd64'],
483+ 'failsafe': {'primary': 'http://fs-primary-intel',
484+ 'security': 'http://fs-security-intel'},
485+ 'search': {
486+ 'primary': ['http://%(ec2_region)s.ec2/',
487+ 'http://%(availability_zone)s.clouds/'],
488+ 'security': ['http://security-mirror1-intel',
489+ 'http://security-mirror2-intel']}},
490+ {'arches': ['armhf', 'armel'],
491+ 'failsafe': {'primary': 'http://fs-primary-arm',
492+ 'security': 'http://fs-security-arm'}},
493+ unknown_arch_info
494+]
495+
496+gpmi = distros._get_package_mirror_info # pylint: disable=W0212
497+gapmi = distros._get_arch_package_mirror_info # pylint: disable=W0212
498+
499+
500+class TestGenericDistro(MockerTestCase):
501+
502+ def return_first(self, mlist):
503+ if not mlist:
504+ return None
505+ return mlist[0]
506+
507+ def return_second(self, mlist):
508+ if not mlist:
509+ return None
510+ return mlist[1]
511+
512+ def return_none(self, _mlist):
513+ return None
514+
515+ def return_last(self, mlist):
516+ if not mlist:
517+ return None
518+ return(mlist[-1])
519+
520+ def setUp(self):
521+ super(TestGenericDistro, self).setUp()
522+ # Make a temp directoy for tests to use.
523+ self.tmp = self.makeDir()
524+
525+ def test_arch_package_mirror_info_unknown(self):
526+ """for an unknown arch, we should get back that with arch 'default'."""
527+ arch_mirrors = gapmi(package_mirrors, arch="unknown")
528+ self.assertEqual(unknown_arch_info, arch_mirrors)
529+
530+ def test_arch_package_mirror_info_known(self):
531+ arch_mirrors = gapmi(package_mirrors, arch="amd64")
532+ self.assertEqual(package_mirrors[0], arch_mirrors)
533+
534+ def test_get_package_mirror_info_az_ec2(self):
535+ arch_mirrors = gapmi(package_mirrors, arch="amd64")
536+
537+ results = gpmi(arch_mirrors, availability_zone="us-east-1a",
538+ mirror_filter=self.return_first)
539+ self.assertEqual(results,
540+ {'primary': 'http://us-east-1.ec2/',
541+ 'security': 'http://security-mirror1-intel'})
542+
543+ results = gpmi(arch_mirrors, availability_zone="us-east-1a",
544+ mirror_filter=self.return_second)
545+ self.assertEqual(results,
546+ {'primary': 'http://us-east-1a.clouds/',
547+ 'security': 'http://security-mirror2-intel'})
548+
549+ results = gpmi(arch_mirrors, availability_zone="us-east-1a",
550+ mirror_filter=self.return_none)
551+ self.assertEqual(results, package_mirrors[0]['failsafe'])
552+
553+ def test_get_package_mirror_info_az_non_ec2(self):
554+ arch_mirrors = gapmi(package_mirrors, arch="amd64")
555+
556+ results = gpmi(arch_mirrors, availability_zone="nova.cloudvendor",
557+ mirror_filter=self.return_first)
558+ self.assertEqual(results,
559+ {'primary': 'http://nova.cloudvendor.clouds/',
560+ 'security': 'http://security-mirror1-intel'})
561+
562+ results = gpmi(arch_mirrors, availability_zone="nova.cloudvendor",
563+ mirror_filter=self.return_last)
564+ self.assertEqual(results,
565+ {'primary': 'http://nova.cloudvendor.clouds/',
566+ 'security': 'http://security-mirror2-intel'})
567+
568+ def test_get_package_mirror_info_none(self):
569+ arch_mirrors = gapmi(package_mirrors, arch="amd64")
570+
571+ # because both search entries here replacement based on
572+ # availability-zone, the filter will be called with an empty list and
573+ # failsafe should be taken.
574+ results = gpmi(arch_mirrors, availability_zone=None,
575+ mirror_filter=self.return_first)
576+ self.assertEqual(results,
577+ {'primary': 'http://fs-primary-intel',
578+ 'security': 'http://security-mirror1-intel'})
579+
580+ results = gpmi(arch_mirrors, availability_zone=None,
581+ mirror_filter=self.return_last)
582+ self.assertEqual(results,
583+ {'primary': 'http://fs-primary-intel',
584+ 'security': 'http://security-mirror2-intel'})
585+
586+
587+#def _get_package_mirror_info(mirror_info, availability_zone=None,
588+# mirror_filter=util.search_for_mirror):
589+
590+
591+# vi: ts=4 expandtab